public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Christophe Lyon <christophe.lyon@st.com>
To: <gcc-patches@gcc.gnu.org>
Cc: <christophe.lyon@linaro.org>
Subject: [ARM/FDPIC v3 06/21] [ARM] FDPIC: Add support for c++ exceptions
Date: Thu, 11 Oct 2018 13:38:00 -0000	[thread overview]
Message-ID: <20181011133518.17258-7-christophe.lyon@st.com> (raw)
In-Reply-To: <20181011133518.17258-1-christophe.lyon@st.com>

The main difference with existing support is that function addresses
are function descriptor addresses instead. This means that all code
dealing with function pointers now has to cope with function
descriptors instead.

For the same reason, Linux kernel helpers can no longer be called by
dereferencing their address, so we implement the same functionality as
a regular function here.

When restoring a function address, we also have to restore the FDPIC
register value (r9).

2018-XX-XX  Christophe Lyon  <christophe.lyon@st.com>
	Mickaël Guêné <mickael.guene@st.com>

	gcc/
	* ginclude/unwind-arm-common.h (unwinder_cache): Add reserved5
	field.
	(FDPIC_REGNUM): New define.

	libgcc/
	* config/arm/linux-atomic.c (__kernel_cmpxchg): Add FDPIC support.
	(__kernel_dmb): Likewise.
	(__fdpic_cmpxchg): New function.
	(__fdpic_dmb): New function.
	* config/arm/unwind-arm.h (gnu_Unwind_Find_got): New function.
	(_Unwind_decode_typeinfo_ptr): Add FDPIC support.
	* unwindo-arm-common.inc (UCB_PR_GOT): New.
	(funcdesc_t): New struct.
	(get_eit_entry): Add FDPIC support.
	(unwind_phase2): Likewise.
	(unwind_phase2_forced): Likewise.
	(__gnu_Unwind_RaiseException): Likewise.
	(__gnu_Unwind_Resume): Likewise.
	(__gnu_Unwind_Backtrace): Likewise.
	* unwind-pe.h (read_encoded_value_with_base): Likewise.

	libstdc++/
	* libsupc++/eh_personality.cc (get_ttype_entry): Add FDPIC
	support.

Change-Id: I517a49ff18fae21c686cd1c6008ea7974515b347

diff --git a/gcc/ginclude/unwind-arm-common.h b/gcc/ginclude/unwind-arm-common.h
index 8a1a919..f663891 100644
--- a/gcc/ginclude/unwind-arm-common.h
+++ b/gcc/ginclude/unwind-arm-common.h
@@ -91,7 +91,7 @@ extern "C" {
 	  _uw reserved2;  /* Personality routine address */
 	  _uw reserved3;  /* Saved callsite address */
 	  _uw reserved4;  /* Forced unwind stop arg */
-	  _uw reserved5;
+	  _uw reserved5;  /* Personality routine GOT value in FDPIC mode.  */
 	}
       unwinder_cache;
       /* Propagation barrier cache (valid after phase 1): */
@@ -247,4 +247,6 @@ typedef unsigned long _uleb128_t;
 }   /* extern "C" */
 #endif
 
+#define FDPIC_REGNUM 9
+
 #endif /* defined UNWIND_ARM_COMMON_H */
diff --git a/libgcc/config/arm/linux-atomic.c b/libgcc/config/arm/linux-atomic.c
index d334c58..161d1ce 100644
--- a/libgcc/config/arm/linux-atomic.c
+++ b/libgcc/config/arm/linux-atomic.c
@@ -25,11 +25,49 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 /* Kernel helper for compare-and-exchange.  */
 typedef int (__kernel_cmpxchg_t) (int oldval, int newval, int *ptr);
+#if __FDPIC__
+/* Non-FDPIC ABIs call __kernel_cmpxchg directly by dereferencing its
+   address, but under FDPIC we would generate a broken call
+   sequence. That's why we have to implement __kernel_cmpxchg and
+   __kernel_dmb here: this way, the FDPIC call sequence works.  */
+#define __kernel_cmpxchg __fdpic_cmpxchg
+#else
 #define __kernel_cmpxchg (*(__kernel_cmpxchg_t *) 0xffff0fc0)
+#endif
 
 /* Kernel helper for memory barrier.  */
 typedef void (__kernel_dmb_t) (void);
+#if __FDPIC__
+#define __kernel_dmb __fdpic_dmb
+#else
 #define __kernel_dmb (*(__kernel_dmb_t *) 0xffff0fa0)
+#endif
+
+#if __FDPIC__
+static int __fdpic_cmpxchg (int oldval, int newval, int *ptr)
+{
+  int result;
+
+  asm volatile ("1: ldrex r3, [%[ptr]]\n\t"
+		"subs  r3, r3, %[oldval]\n\t"
+		"itt eq\n\t"
+		"strexeq r3, %[newval], [%[ptr]]\n\t"
+		"teqeq r3, #1\n\t"
+		"it eq\n\t"
+		"beq 1b\n\t"
+		"rsbs  %[result], r3, #0\n\t"
+		: [result] "=r" (result)
+		: [oldval] "r" (oldval) , [newval] "r" (newval), [ptr] "r" (ptr)
+		: "r3");
+    return result;
+}
+
+static void __fdpic_dmb ()
+{
+  asm volatile ("dmb\n\t");
+}
+
+#endif
 
 /* Note: we implement byte, short and int versions of atomic operations using
    the above kernel helpers; see linux-atomic-64bit.c for "long long" (64-bit)
diff --git a/libgcc/config/arm/unwind-arm.h b/libgcc/config/arm/unwind-arm.h
index 9f7d3f2..db5dfa2 100644
--- a/libgcc/config/arm/unwind-arm.h
+++ b/libgcc/config/arm/unwind-arm.h
@@ -33,9 +33,31 @@
 /* Use IP as a scratch register within the personality routine.  */
 #define UNWIND_POINTER_REG 12
 
+#define STR(x) #x
+#define XSTR(x) STR(x)
+
 #ifdef __cplusplus
 extern "C" {
 #endif
+_Unwind_Ptr __attribute__((weak)) __gnu_Unwind_Find_got (_Unwind_Ptr);
+
+static inline _Unwind_Ptr gnu_Unwind_Find_got (_Unwind_Ptr ptr)
+{
+    _Unwind_Ptr res;
+
+    if (__gnu_Unwind_Find_got)
+	res =  __gnu_Unwind_Find_got (ptr);
+    else
+      {
+	asm volatile ("mov %[result], r" XSTR(FDPIC_REGNUM)
+		      : [result]"=r" (res)
+		      :
+		      :);
+      }
+
+    return res;
+}
+
   /* Decode an R_ARM_TARGET2 relocation.  */
   static inline _Unwind_Word
   _Unwind_decode_typeinfo_ptr (_Unwind_Word base __attribute__ ((unused)),
@@ -48,7 +70,12 @@ extern "C" {
       if (!tmp)
 	return 0;
 
-#if (defined(linux) && !defined(__uClinux__)) || defined(__NetBSD__) \
+#if __FDPIC__
+      /* For FDPIC, we store the offset of the GOT entry.  */
+      /* So, first get GOT from dynamic linker and then use indirect access.  */
+      tmp += gnu_Unwind_Find_got (ptr);
+      tmp = *(_Unwind_Word *) tmp;
+#elif (defined(linux) && !defined(__uClinux__)) || defined(__NetBSD__) \
     || defined(__FreeBSD__) || defined(__fuchsia__)
       /* Pc-relative indirect.  */
 #define _GLIBCXX_OVERRIDE_TTYPE_ENCODING (DW_EH_PE_pcrel | DW_EH_PE_indirect)
diff --git a/libgcc/unwind-arm-common.inc b/libgcc/unwind-arm-common.inc
index 76f8fc3..7187edd 100644
--- a/libgcc/unwind-arm-common.inc
+++ b/libgcc/unwind-arm-common.inc
@@ -62,6 +62,7 @@ __gnu_Unwind_Find_exidx (_Unwind_Ptr, int *);
 #define UCB_PR_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved2)
 #define UCB_SAVED_CALLSITE_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved3)
 #define UCB_FORCED_STOP_ARG(ucbp) ((ucbp)->unwinder_cache.reserved4)
+#define UCB_PR_GOT(ucbp) ((ucbp)->unwinder_cache.reserved5)
 
 /* Unwind descriptors.  */
 
@@ -85,6 +86,15 @@ typedef struct __EIT_entry
   _uw content;
 } __EIT_entry;
 
+#ifdef __FDPIC__
+
+/* Only used in FDPIC case.  */
+struct funcdesc_t {
+    unsigned int ptr;
+    unsigned int got;
+};
+#endif
+
 /* Assembly helper functions.  */
 
 /* Restore core register state.  Never returns.  */
@@ -259,7 +269,21 @@ get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address)
     {
       /* One of the predefined standard routines.  */
       _uw idx = (*(_uw *) ucbp->pr_cache.ehtp >> 24) & 0xf;
+#if __FDPIC__
+      {
+	struct funcdesc_t *funcdesc
+	  = (struct funcdesc_t *) __gnu_unwind_get_pr_addr (idx);
+	if (funcdesc)
+	  {
+	    UCB_PR_ADDR (ucbp) = funcdesc->ptr;
+	    UCB_PR_GOT (ucbp) = funcdesc->got;
+	  }
+	else
+	  UCB_PR_ADDR (ucbp) = 0;
+      }
+#else
       UCB_PR_ADDR (ucbp) = __gnu_unwind_get_pr_addr (idx);
+#endif
       if (UCB_PR_ADDR (ucbp) == 0)
 	{
 	  /* Failed */
@@ -270,6 +294,10 @@ get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address)
     {
       /* Execute region offset to PR */
       UCB_PR_ADDR (ucbp) = selfrel_offset31 (ucbp->pr_cache.ehtp);
+#if __FDPIC__
+      UCB_PR_GOT (ucbp)
+	= (unsigned int) gnu_Unwind_Find_got ((_Unwind_Ptr) UCB_PR_ADDR (ucbp));
+#endif
     }
   return _URC_OK;
 }
@@ -291,14 +319,29 @@ unwind_phase2 (_Unwind_Control_Block * ucbp, phase2_vrs * vrs)
       UCB_SAVED_CALLSITE_ADDR (ucbp) = VRS_PC(vrs);
 
       /* Call the pr to decide what to do.  */
+#if __FDPIC__
+      {
+	volatile struct funcdesc_t funcdesc;
+	funcdesc.ptr = UCB_PR_ADDR (ucbp);
+	funcdesc.got = UCB_PR_GOT (ucbp);
+	pr_result = ((personality_routine) &funcdesc)
+	  (_US_UNWIND_FRAME_STARTING, ucbp, (_Unwind_Context *) vrs);
+      }
+#else
       pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
 	(_US_UNWIND_FRAME_STARTING, ucbp, (_Unwind_Context *) vrs);
+#endif
     }
   while (pr_result == _URC_CONTINUE_UNWIND);
   
   if (pr_result != _URC_INSTALL_CONTEXT)
     abort();
 
+#if __FDPIC__
+  /* r9 could have been lost due to PLT jump.  Restore correct value.  */
+  vrs->core.r[FDPIC_REGNUM] = gnu_Unwind_Find_got (VRS_PC (vrs));
+#endif
+
   uw_restore_core_regs (vrs, &vrs->core);
 }
 
@@ -346,8 +389,18 @@ unwind_phase2_forced (_Unwind_Control_Block *ucbp, phase2_vrs *entry_vrs,
 	  next_vrs = saved_vrs;
 
 	  /* Call the pr to decide what to do.  */
+#if __FDPIC__
+	  {
+	    volatile struct funcdesc_t funcdesc;
+	    funcdesc.ptr = UCB_PR_ADDR (ucbp);
+	    funcdesc.got = UCB_PR_GOT (ucbp);
+	    pr_result = ((personality_routine) &funcdesc)
+	      (action, ucbp, (void *) &next_vrs);
+	  }
+#else
 	  pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
 	    (action, ucbp, (void *) &next_vrs);
+#endif
 
 	  saved_vrs.prev_sp = VRS_SP (&next_vrs);
 	}
@@ -384,6 +437,11 @@ unwind_phase2_forced (_Unwind_Control_Block *ucbp, phase2_vrs *entry_vrs,
       return _URC_FAILURE;
     }
 
+#if __FDPIC__
+  /* r9 could have been lost due to PLT jump.  Restore correct value.  */
+  saved_vrs.core.r[FDPIC_REGNUM] = gnu_Unwind_Find_got (VRS_PC (&saved_vrs));
+#endif
+
   uw_restore_core_regs (&saved_vrs, &saved_vrs.core);
 }
 
@@ -429,8 +487,18 @@ __gnu_Unwind_RaiseException (_Unwind_Control_Block * ucbp,
 	return _URC_FAILURE;
 
       /* Call the pr to decide what to do.  */
+#if __FDPIC__
+      {
+	volatile struct funcdesc_t funcdesc;
+	funcdesc.ptr = UCB_PR_ADDR (ucbp);
+	funcdesc.got = UCB_PR_GOT (ucbp);
+	pr_result = ((personality_routine) &funcdesc)
+	  (_US_VIRTUAL_UNWIND_FRAME, ucbp, (void *) &saved_vrs);
+      }
+#else
       pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
 	(_US_VIRTUAL_UNWIND_FRAME, ucbp, (void *) &saved_vrs);
+#endif
     }
   while (pr_result == _URC_CONTINUE_UNWIND);
 
@@ -488,13 +556,27 @@ __gnu_Unwind_Resume (_Unwind_Control_Block * ucbp, phase2_vrs * entry_vrs)
     }
 
   /* Call the cached PR.  */
+#if __FDPIC__
+  {
+    volatile struct funcdesc_t funcdesc;
+    funcdesc.ptr = UCB_PR_ADDR (ucbp);
+    funcdesc.got = UCB_PR_GOT (ucbp);
+    pr_result = ((personality_routine) &funcdesc)
+      (_US_UNWIND_FRAME_RESUME, ucbp, (_Unwind_Context *) entry_vrs);
+  }
+#else
   pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
 	(_US_UNWIND_FRAME_RESUME, ucbp, (_Unwind_Context *) entry_vrs);
+#endif
 
   switch (pr_result)
     {
     case _URC_INSTALL_CONTEXT:
       /* Upload the registers to enter the landing pad.  */
+#if __FDPIC__
+      /* r9 could have been lost due to PLT jump.  Restore correct value.  */
+      entry_vrs->core.r[FDPIC_REGNUM] = gnu_Unwind_Find_got (VRS_PC (entry_vrs));
+#endif
       uw_restore_core_regs (entry_vrs, &entry_vrs->core);
 
     case _URC_CONTINUE_UNWIND:
@@ -586,9 +668,20 @@ __gnu_Unwind_Backtrace(_Unwind_Trace_Fn trace, void * trace_argument,
 	}
 
       /* Call the pr to decide what to do.  */
+#if __FDPIC__
+      {
+	volatile struct funcdesc_t funcdesc;
+	funcdesc.ptr = UCB_PR_ADDR (ucbp);
+	funcdesc.got = UCB_PR_GOT (ucbp);
+	code = ((personality_routine) &funcdesc)
+	  (_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND,
+	   ucbp, (void *) &saved_vrs);
+      }
+#else
       code = ((personality_routine) UCB_PR_ADDR (ucbp))
 	(_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, 
 	 ucbp, (void *) &saved_vrs);
+#endif
     }
   while (code != _URC_END_OF_STACK
 	 && code != _URC_FAILURE);
diff --git a/libgcc/unwind-pe.h b/libgcc/unwind-pe.h
index dd5ae95..9c5177f 100644
--- a/libgcc/unwind-pe.h
+++ b/libgcc/unwind-pe.h
@@ -259,10 +259,27 @@ read_encoded_value_with_base (unsigned char encoding, _Unwind_Ptr base,
 
       if (result != 0)
 	{
+#if __FDPIC__
+	  /* FDPIC relative addresses imply taking the GOT address
+	     into account.  */
+	  if ((encoding & DW_EH_PE_pcrel) && (encoding & DW_EH_PE_indirect))
+	    {
+	      result += gnu_Unwind_Find_got ((_Unwind_Ptr) u);
+	      result = *(_Unwind_Internal_Ptr *) result;
+	    }
+	  else
+	    {
+	      result += ((encoding & 0x70) == DW_EH_PE_pcrel
+			 ? (_Unwind_Internal_Ptr) u : base);
+	      if (encoding & DW_EH_PE_indirect)
+		result = *(_Unwind_Internal_Ptr *) result;
+	    }
+#else
 	  result += ((encoding & 0x70) == DW_EH_PE_pcrel
 		     ? (_Unwind_Internal_Ptr) u : base);
 	  if (encoding & DW_EH_PE_indirect)
 	    result = *(_Unwind_Internal_Ptr *) result;
+#endif
 	}
     }
 
diff --git a/libstdc++-v3/libsupc++/eh_personality.cc b/libstdc++-v3/libsupc++/eh_personality.cc
index 1b336c7..7b26b8d 100644
--- a/libstdc++-v3/libsupc++/eh_personality.cc
+++ b/libstdc++-v3/libsupc++/eh_personality.cc
@@ -93,7 +93,15 @@ get_ttype_entry (lsda_header_info *info, _uleb128_t i)
   _Unwind_Ptr ptr;
 
   i *= size_of_encoded_value (info->ttype_encoding);
-  read_encoded_value_with_base (info->ttype_encoding, info->ttype_base,
+  read_encoded_value_with_base (
+#if __FDPIC__
+				/* Force these flags to nake sure to
+				   take the GOT into account.  */
+				(DW_EH_PE_pcrel | DW_EH_PE_indirect),
+#else
+				info->ttype_encoding,
+#endif
+				info->ttype_base,
 				info->TType - i, &ptr);
 
   return reinterpret_cast<const std::type_info *>(ptr);
-- 
2.6.3

  parent reply	other threads:[~2018-10-11 13:37 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-11 13:36 [ARM/FDPIC v3 00/21] FDPIC ABI for ARM Christophe Lyon
2018-10-11 13:36 ` [ARM/FDPIC v3 02/21] [ARM] FDPIC: Handle arm*-*-uclinuxfdpiceabi in configure scripts Christophe Lyon
2018-10-12 10:14   ` Richard Earnshaw (lists)
2018-10-15  8:46     ` Christophe Lyon
2018-11-09 12:31       ` Christophe Lyon
2018-10-11 13:36 ` [ARM/FDPIC v3 01/21] [ARM] FDPIC: Add -mfdpic option support Christophe Lyon
2018-10-12 10:01   ` Richard Earnshaw (lists)
2018-10-17  8:13     ` Sandra Loosemore
2018-10-11 13:37 ` [ARM/FDPIC v3 04/21] [ARM] FDPIC: Add support for FDPIC for arm architecture Christophe Lyon
2018-10-12 11:02   ` Richard Earnshaw (lists)
2018-10-19 13:57     ` Christophe Lyon
2018-10-23 15:12       ` Richard Earnshaw (lists)
2018-10-26 16:34         ` Christophe Lyon
2018-10-26 16:41           ` Richard Earnshaw (lists)
2018-10-29 10:31             ` Christophe Lyon
2018-10-11 13:37 ` [ARM/FDPIC v3 03/21] [ARM] FDPIC: Force FDPIC related options unless -mno-fdpic is provided Christophe Lyon
2018-10-12 10:46   ` Richard Earnshaw (lists)
2018-10-15 10:35     ` Christophe Lyon
2018-10-23 15:01       ` Richard Earnshaw (lists)
2018-10-23 15:42         ` Segher Boessenkool
2018-10-26 15:33           ` Christophe Lyon
2018-10-29 14:25             ` Christophe Lyon
2018-10-29 23:15               ` Segher Boessenkool
2018-10-11 13:37 ` [ARM/FDPIC v3 05/21] [ARM] FDPIC: Fix __do_global_dtors_aux and frame_dummy generation Christophe Lyon
2018-10-12 11:29   ` Richard Earnshaw (lists)
2018-10-11 13:38 ` [ARM/FDPIC v3 07/21] [ARM] FDPIC: Avoid saving/restoring r9 on stack since it is RO Christophe Lyon
2018-10-12 12:21   ` Richard Earnshaw (lists)
2018-10-15 14:30     ` Christophe Lyon
2018-10-11 13:38 ` Christophe Lyon [this message]
2018-10-12 11:54   ` [ARM/FDPIC v3 06/21] [ARM] FDPIC: Add support for c++ exceptions Richard Earnshaw (lists)
2018-10-15 14:22     ` Christophe Lyon
2018-10-11 13:38 ` [ARM/FDPIC v3 08/21] [ARM] FDPIC: Ensure local/global binding for function descriptors Christophe Lyon
2018-10-12 12:39   ` Richard Earnshaw (lists)
2018-10-11 13:39 ` [ARM/FDPIC v3 09/21] [ARM] FDPIC: Add support for taking address of nested function Christophe Lyon
2018-10-11 13:39 ` [ARM/FDPIC v3 11/21] [ARM] FDPIC: Add support to unwind FDPIC signal frame Christophe Lyon
2018-10-11 13:39 ` [ARM/FDPIC v3 10/21] [ARM] FDPIC: Implement TLS support Christophe Lyon
2018-10-11 13:40 ` [ARM/FDPIC v3 13/21] [ARM] FDPIC: Force LSB bit for PC in Cortex-M architecture Christophe Lyon
2018-10-11 13:40 ` [ARM/FDPIC v3 12/21] [ARM] FDPIC: Restore r9 after we call __aeabi_read_tp Christophe Lyon
2018-10-19 14:02   ` Christophe Lyon
2018-10-11 13:41 ` [ARM/FDPIC v3 14/21] [ARM][testsuite] FDPIC: Skip unsupported tests Christophe Lyon
2018-10-11 13:41 ` [ARM/FDPIC v3 16/21] [ARM][testsuite] FDPIC: Skip v8-m and v6-m tests that currently produce an ICE Christophe Lyon
2018-10-11 13:41 ` [ARM/FDPIC v3 15/21] [ARM][testsuite] FDPIC: Adjust scan-assembler patterns Christophe Lyon
2018-10-11 13:42 ` [ARM/FDPIC v3 18/21] [ARM][testsuite] FDPIC: Handle *-*-uclinux* Christophe Lyon
2018-10-11 13:42 ` [ARM/FDPIC v3 17/21] [ARM][testsuite] FDPIC: Skip tests that don't work in PIC mode Christophe Lyon
2018-10-11 13:42 ` [ARM/FDPIC v3 19/21] [ARM][testsuite] FDPIC: Enable tests on pie_enabled targets Christophe Lyon
2018-10-11 13:43 ` [ARM/FDPIC v3 20/21] [ARM][testsuite] FDPIC: Adjust pr43698.c to avoid clash with uclibc Christophe Lyon
2018-10-11 13:56 ` [ARM/FDPIC v3 21/21] [ARM][testsuite] FDPIC: Skip tests using architecture older than v7 Christophe Lyon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181011133518.17258-7-christophe.lyon@st.com \
    --to=christophe.lyon@st.com \
    --cc=christophe.lyon@linaro.org \
    --cc=gcc-patches@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).