[Git][ghc/ghc][wip/backports-8.10] 4 commits: rts linker: teach the linker about GLIBC's special handling of *stat, mknod...
Ben Gamari
gitlab at gitlab.haskell.org
Thu Sep 17 19:34:04 UTC 2020
Ben Gamari pushed to branch wip/backports-8.10 at Glasgow Haskell Compiler / GHC
Commits:
721dc35d by Adam Sandberg Ericsson at 2020-09-12T10:19:57+01:00
rts linker: teach the linker about GLIBC's special handling of *stat, mknod and atexit functions #7072
(cherry picked from commit 0effc57d48ace6b719a9f4cbeac67c95ad55010b)
- - - - -
ba74b527 by Ben Gamari at 2020-09-17T15:33:35-04:00
Bump text submodule to 1.2.4.0+
Fixes #18588 and #17956.
- - - - -
ab9debd5 by Ömer Sinan Ağacan at 2020-09-17T15:33:35-04:00
Fix uninitialized field read in Linker.c
Valgrind report of the bug when running the test `linker_unload`:
==29666== Conditional jump or move depends on uninitialised value(s)
==29666== at 0x369C5B4: setOcInitialStatus (Linker.c:1305)
==29666== by 0x369C6C5: mkOc (Linker.c:1347)
==29666== by 0x36C027A: loadArchive_ (LoadArchive.c:522)
==29666== by 0x36C0600: loadArchive (LoadArchive.c:626)
==29666== by 0x2C144CD: ??? (in /home/omer/haskell/ghc_2/testsuite/tests/rts/linker/linker_unload.run/linker_unload)
==29666==
==29666== Conditional jump or move depends on uninitialised value(s)
==29666== at 0x369C5B4: setOcInitialStatus (Linker.c:1305)
==29666== by 0x369C6C5: mkOc (Linker.c:1347)
==29666== by 0x369C9F6: preloadObjectFile (Linker.c:1507)
==29666== by 0x369CA8D: loadObj_ (Linker.c:1536)
==29666== by 0x369CB17: loadObj (Linker.c:1557)
==29666== by 0x3866BC: main (linker_unload.c:33)
The problem is `mkOc` allocates a new `ObjectCode` and calls
`setOcInitialStatus` without initializing the `status` field.
`setOcInitialStatus` reads the field as first thing:
static void setOcInitialStatus(ObjectCode* oc) {
if (oc->status == OBJECT_DONT_RESOLVE)
return;
if (oc->archiveMemberName == NULL) {
oc->status = OBJECT_NEEDED;
} else {
oc->status = OBJECT_LOADED;
}
}
`setOcInitialStatus` is unsed in two places for two different purposes:
in `mkOc` where we don't have the `status` field initialized yet (`mkOc`
is supposed to initialize it), and `loadOc` where we do have `status`
field initialized and we want to update it. Instead of splitting the
function into two functions which are both called just once I inline the
functions in the use sites and remove it.
Fixes #18342
(cherry picked from commit 08c1cb0f30770acbf366423f085f8ef92f7f6a06)
- - - - -
dbe36f62 by GHC GitLab CI at 2020-09-17T15:33:35-04:00
configure: Avoid hard-coded ld path on Windows
The fix to #17962 ended up regressing on Windows as it failed to
replicate the logic responsible for overriding the toolchain paths on
Windows. This resulted in a hard-coded path to a directory that likely
doesn't exist on the user's system (#18550).
(cherry picked from commit 34e0fa963f35a77093fc7111a80c557fc6bd614f)
- - - - -
14 changed files:
- aclocal.m4
- compiler/main/SysTools/Settings.hs
- configure.ac
- hadrian/cfg/system.config.in
- hadrian/src/Builder.hs
- libraries/text
- mk/config.mk.in
- rts/Linker.c
- rules/build-package-way.mk
- testsuite/tests/rts/linker/Makefile
- + testsuite/tests/rts/linker/T7072-main.c
- + testsuite/tests/rts/linker/T7072-obj.c
- + testsuite/tests/rts/linker/T7072.stderr
- testsuite/tests/rts/linker/all.T
Changes:
=====================================
aclocal.m4
=====================================
@@ -516,6 +516,10 @@ AC_DEFUN([FP_SETTINGS],
SettingsHaskellCPPCommand="${mingw_bin_prefix}gcc.exe"
SettingsHaskellCPPFlags="$HaskellCPPArgs"
SettingsLdCommand="${mingw_bin_prefix}ld.exe"
+ # Overrides FIND_MERGE_OBJECTS in order to avoid hard-coding linker
+ # path on Windows (#18550).
+ SettingsMergeObjectsCommand="${SettingsLdCommand}"
+ SettingsMergeObjectsFlags="-r --oformat=pe-bigobj-x86-64"
SettingsArCommand="${mingw_bin_prefix}ar.exe"
SettingsRanlibCommand="${mingw_bin_prefix}ranlib.exe"
SettingsDllWrapCommand="${mingw_bin_prefix}dllwrap.exe"
@@ -529,6 +533,8 @@ AC_DEFUN([FP_SETTINGS],
SettingsHaskellCPPCommand="$(basename $HaskellCPPCmd)"
SettingsHaskellCPPFlags="$HaskellCPPArgs"
SettingsLdCommand="$(basename $LdCmd)"
+ SettingsMergeObjectsCommand="$(basename $MergeObjsCmd)"
+ SettingsMergeObjectsFlags="$MergeObjsArgs"
SettingsArCommand="$(basename $ArCmd)"
SettingsDllWrapCommand="$(basename $DllWrapCmd)"
SettingsWindresCommand="$(basename $WindresCmd)"
@@ -538,6 +544,8 @@ AC_DEFUN([FP_SETTINGS],
SettingsHaskellCPPCommand="$HaskellCPPCmd"
SettingsHaskellCPPFlags="$HaskellCPPArgs"
SettingsLdCommand="$LdCmd"
+ SettingsMergeObjectsCommand="$MergeObjsCmd"
+ SettingsMergeObjectsFlags="$MergeObjsArgs"
SettingsArCommand="$ArCmd"
SettingsRanlibCommand="$RanlibCmd"
if test -z "$DllWrapCmd"
@@ -592,6 +600,8 @@ AC_DEFUN([FP_SETTINGS],
AC_SUBST(SettingsCCompilerSupportsNoPie)
AC_SUBST(SettingsLdCommand)
AC_SUBST(SettingsLdFlags)
+ AC_SUBST(SettingsMergeObjectsCommand)
+ AC_SUBST(SettingsMergeObjectsFlags)
AC_SUBST(SettingsArCommand)
AC_SUBST(SettingsRanlibCommand)
AC_SUBST(SettingsDllWrapCommand)
@@ -2589,7 +2599,7 @@ AC_DEFUN([CHECK_FOR_GOLD_T22266],[
])
$CC -c -o conftest.a.o conftest.a.c || AC_MSG_ERROR([Failed to compile test])
- $SettingsMergeObjectsCommand $SettingsMergeObjectsFlags -T conftest.t conftest.a.o -o conftest.ar.o || AC_MSG_ERROR([Failed to merge test object])
+ $MergeObjsCmd $MergeObjsArgs -T conftest.t conftest.a.o -o conftest.ar.o || AC_MSG_ERROR([Failed to merge test object])
$CC -c -o conftest.main.o conftest.main.c || AC_MSG_ERROR([Failed to compile test driver])
$CC conftest.ar.o conftest.main.o -o conftest || AC_MSG_ERROR([Failed to link test driver])
@@ -2609,33 +2619,30 @@ AC_DEFUN([CHECK_FOR_GOLD_T22266],[
# ------------------
# Find which linker to use to merge object files.
#
+# See Note [Merging object files for GHCi] in GHC.Driver.Pipeline.
AC_DEFUN([FIND_MERGE_OBJECTS],[
AC_REQUIRE([FIND_LD])
- if test -z "$SettingsMergeObjectsCommand"; then
- SettingsMergeObjectsCommand="$LD"
+ if test -z "$MergeObjsCmd"; then
+ MergeObjsCmd="$LD"
fi
- if test -z "$SettingsMergeObjectsFlags"; then
- SettingsMergeObjectsFlags="-r"
+ if test -z "$MergeObjsArgs"; then
+ MergeObjsArgs="-r"
fi
- CHECK_FOR_GOLD_T22266($SettingsMergeObjectsCommand)
+ CHECK_FOR_GOLD_T22266($MergeObjsCmd)
if test "$result" = "1"; then
- AC_MSG_NOTICE([$SettingsMergeObjectsCommand is broken due to binutils 22266, looking for another linker...])
- SettingsMergeObjectsCommand=""
- AC_CHECK_TARGET_TOOL([SettingsMergeObjectsCommand], [ld])
- CHECK_FOR_GOLD_T22266($SettingsMergeObjectsCommand)
+ AC_MSG_NOTICE([$MergeObjsCmd is broken due to binutils 22266, looking for another linker...])
+ MergeObjsCmd=""
+ AC_CHECK_TARGET_TOOL([MergeObjsCmd], [ld])
+ CHECK_FOR_GOLD_T22266($MergeObjsCmd)
if test "$result" = "1"; then
- AC_MSG_ERROR([Linker is affected by binutils 22266 but couldn't find another unaffected linker. Please set the SettingsMergeObjectsCommand variable to a functional linker.])
+ AC_MSG_ERROR([Linker is affected by binutils 22266 but couldn't find another unaffected linker. Please set the MergeObjsCmd variable to a functional linker.])
fi
fi
- if test "$windows" = YES -a "$EnableDistroToolchain" = "NO" -a "$WORD_SIZE" = 64; then
- SettingsMergeObjectsFlags="$SettingsMergeObjectsFlags --oformat=pe-bigobj-x86-64"
- fi
-
- AC_SUBST(SettingsMergeObjectsCommand)
- AC_SUBST(SettingsMergeObjectsFlags)
+ AC_SUBST([MergeObjsCmd])
+ AC_SUBST([MergeObjsArgs])
])
# FIND_PYTHON
=====================================
compiler/main/SysTools/Settings.hs
=====================================
@@ -141,7 +141,7 @@ initSettings top_dir = do
as_args = map Option cc_args
ld_prog = cc_prog
ld_args = map Option (cc_args ++ words cc_link_args_str)
- ld_r_prog <- getSetting "Merge objects command"
+ ld_r_prog <- getToolSetting "Merge objects command"
ld_r_args <- getSetting "Merge objects flags"
llvmTarget <- getSetting "LLVM target"
=====================================
configure.ac
=====================================
@@ -429,6 +429,8 @@ then
NM="${mingwbin}nm.exe"
RANLIB="${mingwbin}ranlib.exe"
OBJDUMP="${mingwbin}objdump.exe"
+ MergeObjsCmd="$LD"
+ MergeObjsArgs="-r --oformat=pe-bigobj-x86-64"
fp_prog_ar="${mingwbin}ar.exe"
AC_PATH_PROG([Genlib],[genlib])
=====================================
hadrian/cfg/system.config.in
=====================================
@@ -14,6 +14,7 @@ hs-cpp = @HaskellCPPCmd@
ld = @LdCmd@
make = @MakeCmd@
nm = @NmCmd@
+merge-objects = @MergeObjsCmd@
objdump = @ObjdumpCmd@
ranlib = @REAL_RANLIB_CMD@
sphinx-build = @SPHINXBUILD@
@@ -114,10 +115,10 @@ conf-ld-linker-args-stage1 = @CONF_LD_LINKER_OPTS_STAGE1@
conf-ld-linker-args-stage2 = @CONF_LD_LINKER_OPTS_STAGE2@
conf-ld-linker-args-stage3 = @CONF_LD_LINKER_OPTS_STAGE3@
-conf-merge-objects-args-stage0 = @SettingsMergeObjectsFlags@
-conf-merge-objects-args-stage1 = @SettingsMergeObjectsFlags@
-conf-merge-objects-args-stage2 = @SettingsMergeObjectsFlags@
-conf-merge-objects-args-stage3 = @SettingsMergeObjectsFlags@
+conf-merge-objects-args-stage0 = @MergeObjsArgs@
+conf-merge-objects-args-stage1 = @MergeObjsArgs@
+conf-merge-objects-args-stage2 = @MergeObjsArgs@
+conf-merge-objects-args-stage3 = @MergeObjsArgs@
# Settings:
=====================================
hadrian/src/Builder.hs
=====================================
@@ -313,7 +313,7 @@ systemBuilderPath builder = case builder of
Happy -> fromKey "happy"
HsCpp -> fromKey "hs-cpp"
Ld _ -> fromKey "ld"
- MergeObjects _ -> fromKey "settings-merge-objects-command"
+ MergeObjects _ -> fromKey "merge-objects"
Make _ -> fromKey "make"
Makeinfo -> fromKey "makeinfo"
Nm -> fromKey "nm"
=====================================
libraries/text
=====================================
@@ -1 +1 @@
-Subproject commit c6768a2a07e94b8b26d0f0e53517773de1110ce2
+Subproject commit e07c14940c25f33fe5b282912d745d3a79dd4ade
=====================================
mk/config.mk.in
=====================================
@@ -556,6 +556,16 @@ LD_STAGE1 = $(LD)
LD_STAGE2 = $(LD)
LD_STAGE3 = $(LD)
+MERGE_OBJS_STAGE0 = @MergeObjsCmd@
+MERGE_OBJS_STAGE1 = @MergeObjsCmd@
+MERGE_OBJS_STAGE2 = @MergeObjsCmd@
+MERGE_OBJS_STAGE3 = @MergeObjsCmd@
+
+MERGE_OBJS_STAGE0_FLAGS = @MergeObjsArgs@
+MERGE_OBJS_STAGE1_FLAGS = @MergeObjsArgs@
+MERGE_OBJS_STAGE2_FLAGS = @MergeObjsArgs@
+MERGE_OBJS_STAGE3_FLAGS = @MergeObjsArgs@
+
# Cross-compiling options
# See Note [CrossCompiling vs Stage1Only]
CrossCompiling = @CrossCompiling@
=====================================
rts/Linker.c
=====================================
@@ -655,23 +655,51 @@ internal_dlsym(const char *symbol) {
// We acquire dl_mutex as concurrent dl* calls may alter dlerror
ACQUIRE_LOCK(&dl_mutex);
+
+ // clears dlerror
dlerror();
+
// look in program first
v = dlsym(dl_prog_handle, symbol);
if (dlerror() == NULL) {
RELEASE_LOCK(&dl_mutex);
+ IF_DEBUG(linker, debugBelch("internal_dlsym: found symbol '%s' in program\n", symbol));
return v;
}
for (o_so = openedSOs; o_so != NULL; o_so = o_so->next) {
v = dlsym(o_so->handle, symbol);
if (dlerror() == NULL) {
+ IF_DEBUG(linker, debugBelch("internal_dlsym: found symbol '%s' in shared object\n", symbol));
RELEASE_LOCK(&dl_mutex);
return v;
}
}
RELEASE_LOCK(&dl_mutex);
- return v;
+
+# if defined(HAVE_SYS_STAT_H) && defined(linux_HOST_OS) && defined(__GLIBC__)
+ // HACK: GLIBC implements these functions with a great deal of trickery where
+ // they are either inlined at compile time to their corresponding
+ // __xxxx(SYS_VER, ...) function or direct syscalls, or resolved at
+ // link time via libc_nonshared.a.
+ //
+ // We borrow the approach that the LLVM JIT uses to resolve these
+ // symbols. See http://llvm.org/PR274 and #7072 for more info.
+
+ IF_DEBUG(linker, debugBelch("internal_dlsym: looking for symbol '%s' in GLIBC special cases\n", symbol));
+
+ if (strcmp(symbol, "stat") == 0) return (void*)&stat;
+ if (strcmp(symbol, "fstat") == 0) return (void*)&fstat;
+ if (strcmp(symbol, "lstat") == 0) return (void*)&lstat;
+ if (strcmp(symbol, "stat64") == 0) return (void*)&stat64;
+ if (strcmp(symbol, "fstat64") == 0) return (void*)&fstat64;
+ if (strcmp(symbol, "lstat64") == 0) return (void*)&lstat64;
+ if (strcmp(symbol, "atexit") == 0) return (void*)&atexit;
+ if (strcmp(symbol, "mknod") == 0) return (void*)&mknod;
+# endif
+
+ // we failed to find the symbol
+ return NULL;
}
# endif
@@ -847,13 +875,13 @@ SymbolAddr* lookupSymbol_ (SymbolName* lbl)
SymbolAddr* lookupSymbol_ (SymbolName* lbl)
{
- IF_DEBUG(linker, debugBelch("lookupSymbol: looking up %s\n", lbl));
+ IF_DEBUG(linker, debugBelch("lookupSymbol: looking up '%s'\n", lbl));
ASSERT(symhash != NULL);
RtsSymbolInfo *pinfo;
if (!ghciLookupSymbolInfo(symhash, lbl, &pinfo)) {
- IF_DEBUG(linker, debugBelch("lookupSymbol: symbol not found\n"));
+ IF_DEBUG(linker, debugBelch("lookupSymbol: symbol '%s' not found, trying dlsym\n", lbl));
# if defined(OBJFORMAT_ELF)
return internal_dlsym(lbl);
@@ -1343,23 +1371,6 @@ void freeObjectCode (ObjectCode *oc)
stgFree(oc);
}
-/* -----------------------------------------------------------------------------
-* Sets the initial status of a fresh ObjectCode
-*/
-static void setOcInitialStatus(ObjectCode* oc) {
- /* If a target has requested the ObjectCode not to be resolved then
- honor this requests. Usually this means the ObjectCode has not been
- initialized and can't be. */
- if (oc->status == OBJECT_DONT_RESOLVE)
- return;
-
- if (oc->archiveMemberName == NULL) {
- oc->status = OBJECT_NEEDED;
- } else {
- oc->status = OBJECT_LOADED;
- }
-}
-
ObjectCode*
mkOc( pathchar *path, char *image, int imageSize,
bool mapped, char *archiveMemberName, int misalignment ) {
@@ -1392,7 +1403,11 @@ mkOc( pathchar *path, char *image, int imageSize,
oc->archiveMemberName = NULL;
}
- setOcInitialStatus( oc );
+ if (oc->archiveMemberName == NULL) {
+ oc->status = OBJECT_NEEDED;
+ } else {
+ oc->status = OBJECT_LOADED;
+ }
oc->fileSize = imageSize;
oc->symbols = NULL;
@@ -1683,8 +1698,17 @@ HsInt loadOc (ObjectCode* oc)
# endif
#endif
- /* loaded, but not resolved yet, ensure the OC is in a consistent state */
- setOcInitialStatus( oc );
+ /* Loaded, but not resolved yet, ensure the OC is in a consistent state.
+ If a target has requested the ObjectCode not to be resolved then honor
+ this requests. Usually this means the ObjectCode has not been initialized
+ and can't be. */
+ if (oc->status != OBJECT_DONT_RESOLVE) {
+ if (oc->archiveMemberName == NULL) {
+ oc->status = OBJECT_NEEDED;
+ } else {
+ oc->status = OBJECT_LOADED;
+ }
+ }
IF_DEBUG(linker, debugBelch("loadOc: done.\n"));
return 1;
=====================================
rules/build-package-way.mk
=====================================
@@ -107,6 +107,7 @@ endif
endif
# Build the GHCi library
+# See Note [Merging object files for GHCi] in GHC.Driver.Pipeline.
ifneq "$(filter $3, v p)" ""
$1_$2_$3_GHCI_LIB = $1/$2/build/HS$$($1_$2_COMPONENT_ID).$$($3_osuf)
ifeq "$$($1_$2_BUILD_GHCI_LIB)" "YES"
@@ -116,7 +117,7 @@ BINDIST_LIBS += $$($1_$2_$3_GHCI_LIB)
endif
endif
$$($1_$2_$3_GHCI_LIB) : $$($1_$2_$3_HS_OBJS) $$($1_$2_$3_CMM_OBJS) $$($1_$2_$3_C_OBJS) $$($1_$2_$3_S_OBJS) $$($1_$2_EXTRA_OBJS) $$($1_$2_LD_SCRIPT)
- $$(call cmd,SettingsMergeObjectsCommand) $(SettingsMergeObjectsFlags) $$(if $$($1_$2_LD_SCRIPT),$$($1_$2_LD_SCRIPT_CMD) $$($1_$2_LD_SCRIPT)) -o $$@ $$(EXTRA_LD_LINKER_OPTS) $$($1_$2_$3_HS_OBJS) $$($1_$2_$3_CMM_OBJS) $$($1_$2_$3_C_OBJS) $$($1_$2_$3_S_OBJS) $$($1_$2_EXTRA_OBJS)
+ $$(call cmd,MERGE_OBJS_STAGE$4) $(MERGE_OBJS_STAGE$4_FLAGS) $$(if $$($1_$2_LD_SCRIPT),$$($1_$2_LD_SCRIPT_CMD) $$($1_$2_LD_SCRIPT)) -o $$@ $$(EXTRA_LD_LINKER_OPTS) $$($1_$2_$3_HS_OBJS) $$($1_$2_$3_CMM_OBJS) $$($1_$2_$3_C_OBJS) $$($1_$2_$3_S_OBJS) $$($1_$2_EXTRA_OBJS)
ifeq "$$($1_$2_BUILD_GHCI_LIB)" "YES"
# Don't bother making ghci libs for bootstrapping packages
ifneq "$4" "0"
=====================================
testsuite/tests/rts/linker/Makefile
=====================================
@@ -96,3 +96,10 @@ linker_error3:
"$(TEST_HC)" -c linker_error3.c -o linker_error3_o.o
"$(TEST_HC)" linker_error3.o -o linker_error3 -no-hs-main -optc-g -debug -threaded
./linker_error3 linker_error3_o.o
+
+.PHONY: T7072
+T7072:
+ "$(TEST_HC)" -c T7072-obj.c -o T7072-obj.o
+ "$(TEST_HC)" -c T7072-main.c -o T7072-main.o
+ "$(TEST_HC)" T7072-main.c -o T7072-main -no-hs-main -debug
+ ./T7072-main T7072-obj.o
=====================================
testsuite/tests/rts/linker/T7072-main.c
=====================================
@@ -0,0 +1,39 @@
+#include "ghcconfig.h"
+#include "Rts.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+int main (int argc, char *argv[])
+{
+ int r;
+ char *obj;
+
+ hs_init(&argc, &argv);
+
+ initLinker_(0);
+
+ // Load object file argv[1] repeatedly
+
+ if (argc != 2) {
+ errorBelch("usage: T7072-main <object-file>");
+ exit(1);
+ }
+
+ obj = argv[1];
+
+ r = loadObj(obj);
+ if (!r) {
+ debugBelch("loadObj(%s) failed\n", obj);
+ exit(1);
+ }
+ r = resolveObjs();
+ if (!r) {
+ debugBelch("resolveObjs failed\n");
+ unloadObj(obj);
+ exit(1);
+ }
+ debugBelch("loading succeeded");
+
+ hs_exit();
+ return 0;
+}
=====================================
testsuite/tests/rts/linker/T7072-obj.c
=====================================
@@ -0,0 +1,17 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+
+typedef int stat_func(const char*, struct stat*);
+
+stat_func *foo = &stat;
+
+void stat_test(void)
+{
+ struct stat buf;
+
+ printf("About to stat-test.c\n");
+ foo("stat-test.c", &buf);
+ printf("Done\n");
+}
=====================================
testsuite/tests/rts/linker/T7072.stderr
=====================================
@@ -0,0 +1 @@
+loading succeeded
\ No newline at end of file
=====================================
testsuite/tests/rts/linker/all.T
=====================================
@@ -92,3 +92,10 @@ test('rdynamic', [ unless(opsys('linux') or opsys('mingw32'), skip)
, omit_ways(['ghci'])
],
compile_and_run, ['-rdynamic -package ghc'])
+
+
+test('T7072',
+ [extra_files(['T7072-main.c', 'T7072-obj.c']),
+ unless(opsys('linux'), skip),
+ req_rts_linker],
+ makefile_test, ['T7072'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/bd2af2c6a4f21d233499c33fb71d029ed0c1e4f3...dbe36f62e27f61fd4e1bb99ce8bf71783bd713da
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/bd2af2c6a4f21d233499c33fb71d029ed0c1e4f3...dbe36f62e27f61fd4e1bb99ce8bf71783bd713da
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20200917/f27e5e6f/attachment-0001.html>
More information about the ghc-commits
mailing list