From: Jiri Slaby <jslaby@suse.cz>
To: mingo@redhat.com
Cc: tglx@linutronix.de, hpa@zytor.com, x86@kernel.org,
linux-kernel@vger.kernel.org, Jiri Slaby <jslaby@suse.cz>,
Andrew Morton <akpm@linux-foundation.org>,
Boris Ostrovsky <boris.ostrovsky@oracle.com>,
Ingo Molnar <mingo@kernel.org>,
jpoimboe@redhat.com, Juergen Gross <jgross@suse.com>,
Len Brown <len.brown@intel.com>,
Linus Torvalds <torvalds@linux-foundation.org>,
linux-pm@vger.kernel.org, Pavel Machek <pavel@ucw.cz>,
Peter Zijlstra <a.p.zijlstra@chello.nl>,
"Rafael J. Wysocki" <rjw@rjwysocki.net>,
xen-devel@lists.xenproject.org
Subject: [PATCH v4 01/27] linkage: new macros for assembler symbols
Date: Mon, 2 Oct 2017 11:12:20 +0200 [thread overview]
Message-ID: <20171002091246.28432-1-jslaby@suse.cz> (raw)
Introduce new C macros for annotations of functions and data in
assembly. There is a long-standing mess in macros like ENTRY, END,
ENDPROC and similar. They are used in different manners and sometimes
incorrectly.
So introduce macros with clear use to annotate assembly as follows:
a) Support macros for the ones below
SYM_T_FUNC -- type used by assembler to mark functions
SYM_T_OBJECT -- type used by assembler to mark data
SYM_T_NONE -- type used by assembler to mark entries of unknown type
They are defined as STT_FUNC, STT_OBJECT, and STT_NOTYPE
respectively. According to the gas manual, this is the most portable
way. I am not sure about other assemblers, so we can switch this back
to %function and %object if this turns into a problem. Architectures
can also override them by something like ", @function" if they need.
SYM_A_ALIGN, SYM_A_NONE -- align the symbol?
SYM_V_GLOBAL, SYM_V_WEAK, SYM_V_LOCAL -- visibility of symbols
b) Mostly internal annotations, used by the ones below
SYM_ENTRY -- use only if you have to (for non-paired symbols)
SYM_START -- use only if you have to (for paired symbols)
SYM_END -- use only if you have to (for paired symbols)
c) Annotations for code
SYM_FUNC_START_LOCAL_ALIAS -- use where there are two local names for
one function
SYM_FUNC_START_ALIAS -- use where there are two global names for one
function
SYM_FUNC_END_ALIAS -- the end of LOCAL_ALIASed or ALIASed function
SYM_FUNC_START -- use for global functions
SYM_FUNC_START_NOALIGN -- use for global functions, w/o alignment
SYM_FUNC_START_LOCAL -- use for local functions
SYM_FUNC_START_LOCAL_NOALIGN -- use for local functions, w/o
alignment
SYM_FUNC_START_WEAK -- use for weak functions
SYM_FUNC_START_WEAK_NOALIGN -- use for weak functions, w/o alignment
SYM_FUNC_END -- the end of SYM_FUNC_START_LOCAL, SYM_FUNC_START,
SYM_FUNC_START_WEAK, ...
SYM_FUNC_INNER_LABEL -- only for labels in the middle of functions
SYM_FUNC_INNER_LABEL_NOALIGN -- only for labels in the middle of
functions, w/o alignment
For functions with special (non-C) calling conventions:
SYM_CODE_START -- use for non-C (special) functions
SYM_CODE_START_NOALIGN -- use for non-C (special) functions, w/o
alignment
SYM_CODE_START_LOCAL -- use for local non-C (special) functions
SYM_CODE_START_LOCAL_NOALIGN -- use for local non-C (special)
functions, w/o alignment
SYM_CODE_END -- the end of SYM_CODE_START_LOCAL or SYM_CODE_START
SYM_CODE_INNER_LABEL -- only for labels in the middle of code
SYM_CODE_INNER_LABEL_NOALIGN -- only for labels in the middle of code
d) For data
SYM_DATA_START -- global data symbol
SYM_DATA_END -- the end of the SYM_DATA_START symbol
SYM_DATA_END_LABEL -- the labeled end of SYM_DATA_START symbol
SYM_DATA_SIMPLE -- start+end wrapper around simple global data
SYM_DATA_SIMPLE_LOCAL -- start+end wrapper around simple local data
==========
The macros allow to pair starts and ends of functions and mark functions
correctly in the output ELF objects.
All users of the old macros in x86 are converted to use these in further
patches.
[v2]
* use SYM_ prefix and sane names
* add SYM_START and SYM_END and parametrize all the macros
[v3]
* add SYM_DATA_SIMPLE, SYM_DATA_SIMPLE_LOCAL, and SYM_DATA_END_LABEL
[v4]
* add _NOALIGN versions of some macros
* add _CODE_ derivates of _FUNC_ macros
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: hpa@zytor.com
Cc: Ingo Molnar <mingo@kernel.org>
Cc: jpoimboe@redhat.com
Cc: Juergen Gross <jgross@suse.com>
Cc: Len Brown <len.brown@intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: linux-kernel@vger.kernel.org
Cc: linux-pm@vger.kernel.org
Cc: mingo@redhat.com
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: xen-devel@lists.xenproject.org
Cc: x86@kernel.org
---
arch/x86/include/asm/linkage.h | 10 +-
include/linux/linkage.h | 257 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 257 insertions(+), 10 deletions(-)
diff --git a/arch/x86/include/asm/linkage.h b/arch/x86/include/asm/linkage.h
index 0ccb26dda126..1d22926bb313 100644
--- a/arch/x86/include/asm/linkage.h
+++ b/arch/x86/include/asm/linkage.h
@@ -12,9 +12,13 @@
#ifdef __ASSEMBLY__
-#define GLOBAL(name) \
- .globl name; \
- name:
+/*
+ * GLOBAL is DEPRECATED
+ *
+ * use SYM_DATA_START, SYM_FUNC_START, SYM_FUNC_INNER_LABEL, SYM_CODE_START, or
+ * similar
+ */
+#define GLOBAL(name) SYM_ENTRY(name, SYM_V_GLOBAL, SYM_A_NONE)
#if defined(CONFIG_X86_64) || defined(CONFIG_X86_ALIGNMENT_16)
#define __ALIGN .p2align 4, 0x90
diff --git a/include/linux/linkage.h b/include/linux/linkage.h
index a6a42dd02466..05b5591b5cf7 100644
--- a/include/linux/linkage.h
+++ b/include/linux/linkage.h
@@ -74,25 +74,51 @@
#ifdef __ASSEMBLY__
+/* SYM_T_FUNC -- type used by assembler to mark functions */
+#ifndef SYM_T_FUNC
+#define SYM_T_FUNC STT_FUNC
+#endif
+
+/* SYM_T_OBJECT -- type used by assembler to mark data */
+#ifndef SYM_T_OBJECT
+#define SYM_T_OBJECT STT_OBJECT
+#endif
+
+/* SYM_T_NONE -- type used by assembler to mark entries of unknown type */
+#ifndef SYM_T_NONE
+#define SYM_T_NONE STT_NOTYPE
+#endif
+
+/* SYM_A_* -- align the symbol? */
+#define SYM_A_ALIGN ALIGN
+#define SYM_A_NONE /* nothing */
+
+/* SYM_V_* -- visibility of symbols */
+#define SYM_V_GLOBAL(name) .globl name
+#define SYM_V_WEAK(name) .weak name
+#define SYM_V_LOCAL(name) /* nothing */
+
#ifndef LINKER_SCRIPT
#define ALIGN __ALIGN
#define ALIGN_STR __ALIGN_STR
+/* === DEPRECATED annotations === */
+
#ifndef ENTRY
+/* deprecated, use SYM_FUNC_START */
#define ENTRY(name) \
- .globl name ASM_NL \
- ALIGN ASM_NL \
- name:
+ SYM_FUNC_START(name)
#endif
#endif /* LINKER_SCRIPT */
#ifndef WEAK
+/* deprecated, use SYM_FUNC_START_WEAK* */
#define WEAK(name) \
- .weak name ASM_NL \
- name:
+ SYM_FUNC_START_WEAK_NOALIGN(name)
#endif
#ifndef END
+/* deprecated, use SYM_FUNC_END, SYM_DATA_END, or SYM_END */
#define END(name) \
.size name, .-name
#endif
@@ -102,11 +128,228 @@
* static analysis tools such as stack depth analyzer.
*/
#ifndef ENDPROC
+/* deprecated, use SYM_FUNC_END */
#define ENDPROC(name) \
- .type name, @function ASM_NL \
- END(name)
+ SYM_FUNC_END(name)
#endif
+/* === generic annotations === */
+
+/* SYM_ENTRY -- use only if you have to for non-paired symbols */
+#ifndef SYM_ENTRY
+#define SYM_ENTRY(name, visibility, align...) \
+ visibility(name) ASM_NL \
+ align ASM_NL \
+ name:
+#endif
+
+/* SYM_START -- use only if you have to */
+#ifndef SYM_START
+#define SYM_START(name, visibility, align...) \
+ SYM_ENTRY(name, visibility, align)
#endif
+/* SYM_END -- use only if you have to */
+#ifndef SYM_END
+#define SYM_END(name, sym_type) \
+ .type name sym_type ASM_NL \
+ .size name, .-name
#endif
+
+/* === code annotations === */
+
+/*
+ * FUNC -- C-like functions (proper stack frame etc.)
+ * CODE -- non-C code (e.g. irq handlers with different, special stack etc.)
+ *
+ * Objtool validates stack for FUNC, but not for CODE.
+ * Objtool generates debug info for both FUNC & CODE, but needs special
+ * annotations for each CODE's start (to describe the actual stack frame).
+ *
+ * ALIAS -- does not generate debug info -- the aliased function will
+ */
+
+/*
+ * SYM_FUNC_START_LOCAL_ALIAS -- use where there are two local names for one
+ * function
+ */
+#ifndef SYM_FUNC_START_LOCAL_ALIAS
+#define SYM_FUNC_START_LOCAL_ALIAS(name) \
+ SYM_START(name, SYM_V_LOCAL, SYM_A_ALIGN)
+#endif
+
+/*
+ * SYM_FUNC_START_ALIAS -- use where there are two global names for one
+ * function
+ */
+#ifndef SYM_FUNC_START_ALIAS
+#define SYM_FUNC_START_ALIAS(name) \
+ SYM_START(name, SYM_V_GLOBAL, SYM_A_ALIGN)
+#endif
+
+/* SYM_FUNC_START -- use for global functions */
+#ifndef SYM_FUNC_START
+/*
+ * The same as SYM_FUNC_START_ALIAS, but we will need to distinguish these two
+ * later.
+ */
+#define SYM_FUNC_START(name) \
+ SYM_START(name, SYM_V_GLOBAL, SYM_A_ALIGN)
+#endif
+
+/* SYM_FUNC_START_NOALIGN -- use for global functions, w/o alignment */
+#ifndef SYM_FUNC_START_NOALIGN
+#define SYM_FUNC_START_NOALIGN(name) \
+ SYM_START(name, SYM_V_GLOBAL, SYM_A_NONE)
+#endif
+
+/* SYM_FUNC_START_LOCAL -- use for local functions */
+#ifndef SYM_FUNC_START_LOCAL
+/* the same as SYM_FUNC_START_LOCAL_ALIAS, see comment near SYM_FUNC_START */
+#define SYM_FUNC_START_LOCAL(name) \
+ SYM_START(name, SYM_V_LOCAL, SYM_A_ALIGN)
+#endif
+
+/* SYM_FUNC_START_LOCAL_NOALIGN -- use for local functions, w/o alignment */
+#ifndef SYM_FUNC_START_LOCAL_NOALIGN
+#define SYM_FUNC_START_LOCAL_NOALIGN(name) \
+ SYM_START(name, SYM_V_LOCAL, SYM_A_NONE)
+#endif
+
+/* SYM_FUNC_START_WEAK -- use for weak functions */
+#ifndef SYM_FUNC_START_WEAK
+#define SYM_FUNC_START_WEAK(name) \
+ SYM_START(name, SYM_V_WEAK, SYM_A_ALIGN)
+#endif
+
+/* SYM_FUNC_START_WEAK_NOALIGN -- use for weak functions, w/o alignment */
+#ifndef SYM_FUNC_START_WEAK_NOALIGN
+#define SYM_FUNC_START_WEAK_NOALIGN(name) \
+ SYM_START(name, SYM_V_WEAK, SYM_A_NONE)
+#endif
+
+/* SYM_FUNC_END_ALIAS -- the end of LOCAL_ALIASed or ALIASed function */
+#ifndef SYM_FUNC_END_ALIAS
+#define SYM_FUNC_END_ALIAS(name) \
+ SYM_END(name, SYM_T_FUNC)
+#endif
+
+/*
+ * SYM_FUNC_END -- the end of SYM_FUNC_START_LOCAL, SYM_FUNC_START,
+ * SYM_FUNC_START_WEAK, ...
+ */
+#ifndef SYM_FUNC_END
+/* the same as SYM_FUNC_END_ALIAS, see comment near SYM_FUNC_START */
+#define SYM_FUNC_END(name) \
+ SYM_END(name, SYM_T_FUNC)
+#endif
+
+/* SYM_FUNC_INNER_LABEL -- only for labels in the middle of functions */
+#ifndef SYM_FUNC_INNER_LABEL
+#define SYM_FUNC_INNER_LABEL(name, visibility) \
+ .type name SYM_T_FUNC ASM_NL \
+ SYM_ENTRY(name, visibility, SYM_A_ALIGN)
+#endif
+
+/* SYM_FUNC_INNER_LABEL_NOALIGN -- only for labels in the middle of functions */
+#ifndef SYM_FUNC_INNER_LABEL_NOALIGN
+#define SYM_FUNC_INNER_LABEL_NOALIGN(name, visibility) \
+ .type name SYM_T_FUNC ASM_NL \
+ SYM_ENTRY(name, visibility, SYM_A_NONE)
+#endif
+
+/* SYM_CODE_START -- use for non-C (special) functions */
+#ifndef SYM_CODE_START
+#define SYM_CODE_START(name) \
+ SYM_START(name, SYM_V_GLOBAL, SYM_A_ALIGN)
+#endif
+
+/* SYM_CODE_START_NOALIGN -- use for non-C (special) functions, w/o alignment */
+#ifndef SYM_CODE_START_NOALIGN
+#define SYM_CODE_START_NOALIGN(name) \
+ SYM_START(name, SYM_V_GLOBAL, SYM_A_NONE)
+#endif
+
+/* SYM_CODE_START_LOCAL -- use for local non-C (special) functions */
+#ifndef SYM_CODE_START_LOCAL
+#define SYM_CODE_START_LOCAL(name) \
+ SYM_START(name, SYM_V_LOCAL, SYM_A_ALIGN)
+#endif
+
+/*
+ * SYM_CODE_START_LOCAL_NOALIGN -- use for local non-C (special) functions,
+ * w/o alignment
+ */
+#ifndef SYM_CODE_START_LOCAL_NOALIGN
+#define SYM_CODE_START_LOCAL_NOALIGN(name) \
+ SYM_START(name, SYM_V_LOCAL, SYM_A_NONE)
+#endif
+
+/* SYM_CODE_END -- the end of SYM_CODE_START_LOCAL, SYM_CODE_START, ... */
+#ifndef SYM_CODE_END
+#define SYM_CODE_END(name) \
+ SYM_END(name, SYM_T_NONE)
+#endif
+
+/* SYM_CODE_INNER_LABEL -- only for labels in the middle of code */
+#ifndef SYM_CODE_INNER_LABEL
+#define SYM_CODE_INNER_LABEL(name, visibility) \
+ .type name SYM_T_NONE ASM_NL \
+ SYM_ENTRY(name, visibility, SYM_A_ALIGN)
+#endif
+
+/* SYM_CODE_INNER_LABEL_NOALIGN -- only for labels in the middle of code */
+#ifndef SYM_CODE_INNER_LABEL_NOALIGN
+#define SYM_CODE_INNER_LABEL_NOALIGN(name, visibility) \
+ .type name SYM_T_NONE ASM_NL \
+ SYM_ENTRY(name, visibility, SYM_A_NONE)
+#endif
+
+/* === data annotations === */
+
+/* SYM_DATA_START -- global data symbol */
+#ifndef SYM_DATA_START
+#define SYM_DATA_START(name) \
+ SYM_START(name, SYM_V_GLOBAL, SYM_A_NONE)
+#endif
+
+/* SYM_DATA_START -- local data symbol */
+#ifndef SYM_DATA_START_LOCAL
+#define SYM_DATA_START_LOCAL(name) \
+ SYM_START(name, SYM_V_LOCAL, SYM_A_NONE)
+#endif
+
+/* SYM_DATA_END -- the end of SYM_DATA_START symbol */
+#ifndef SYM_DATA_END
+#define SYM_DATA_END(name) \
+ SYM_END(name, SYM_T_OBJECT)
+#endif
+
+/* SYM_DATA_END_LABEL -- the labeled end of SYM_DATA_START symbol */
+#ifndef SYM_DATA_END_LABEL
+#define SYM_DATA_END_LABEL(name, visibility, label) \
+ visibility(label) ASM_NL \
+ .type label SYM_T_OBJECT ASM_NL \
+ label: \
+ SYM_END(name, SYM_T_OBJECT)
+#endif
+
+/* SYM_DATA_SIMPLE -- start+end wrapper around simple global data */
+#ifndef SYM_DATA_SIMPLE
+#define SYM_DATA_SIMPLE(name, data) \
+ SYM_DATA_START(name) ASM_NL \
+ data ASM_NL \
+ SYM_DATA_END(name)
+#endif
+
+/* SYM_DATA_SIMPLE_LOCAL -- start+end wrapper around simple local data */
+#ifndef SYM_DATA_SIMPLE_LOCAL
+#define SYM_DATA_SIMPLE_LOCAL(name, data...) \
+ SYM_DATA_START_LOCAL(name) ASM_NL \
+ data ASM_NL \
+ SYM_DATA_END(name)
+#endif
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _LINUX_LINKAGE_H */
--
2.14.2
next reply other threads:[~2017-10-02 9:18 UTC|newest]
Thread overview: 45+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-10-02 9:12 Jiri Slaby [this message]
2017-10-02 9:12 ` [PATCH v4 02/27] x86: assembly, use DATA_SIMPLE for data Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 03/27] x86: assembly, annotate relocate_kernel Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 04/27] x86: entry, annotate THUNKs Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 05/27] x86: assembly, annotate local pseudo-functions Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 06/27] x86: crypto, annotate local functions Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 07/27] x86: boot, " Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 08/27] x86: assembly, annotate aliases Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 09/27] x86: entry, annotate interrupt symbols properly Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 10/27] x86: head, annotate data appropriatelly Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 11/27] x86: boot, " Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 12/27] x86: um, " Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 13/27] x86: xen-pvh, " Jiri Slaby
2017-10-02 16:49 ` Boris Ostrovsky
2017-10-02 9:12 ` [PATCH v4 14/27] x86: purgatory, start using annotations Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 15/27] x86: assembly, do not annotate functions by GLOBAL Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 16/27] x86: assembly, use SYM_CODE_INNER_LABEL_NOALIGN instead of GLOBAL Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 17/27] x86: realmode, use SYM_DATA_* " Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 18/27] x86: assembly, remove GLOBAL macro Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 19/27] x86: assembly, make some functions local Jiri Slaby
2017-10-02 12:48 ` Ard Biesheuvel
2017-10-04 7:22 ` Jiri Slaby
2017-10-04 7:33 ` Ard Biesheuvel
2017-10-06 12:53 ` Jiri Slaby
2017-10-06 13:21 ` Mark Rutland
2017-10-25 14:21 ` Jiri Slaby
2017-10-25 14:46 ` Mark Rutland
2017-10-06 14:01 ` Ard Biesheuvel
2017-10-25 14:18 ` Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 20/27] x86: ftrace, mark function_hook as function Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 21/27] x86_64: assembly, add ENDs to some functions and relabel with SYM_CODE_* Jiri Slaby
2017-10-02 16:59 ` Boris Ostrovsky
2017-10-02 9:12 ` [PATCH v4 22/27] x86_64: assembly, change all ENTRY+END to SYM_CODE_* Jiri Slaby
2017-10-02 18:13 ` Boris Ostrovsky
2017-10-02 9:12 ` [PATCH v4 23/27] x86_64: assembly, change all ENTRY+ENDPROC to SYM_FUNC_* Jiri Slaby
2017-10-02 12:30 ` Rafael J. Wysocki
2017-10-02 18:16 ` Boris Ostrovsky
2017-10-02 9:12 ` [PATCH v4 24/27] x86_32: assembly, add ENDs to some functions and relabel with SYM_CODE_* Jiri Slaby
2017-10-02 18:16 ` Boris Ostrovsky
2017-10-03 0:26 ` Rafael J. Wysocki
2017-10-02 9:12 ` [PATCH v4 25/27] x86_32: assembly, change all ENTRY+END to SYM_CODE_* Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 26/27] x86_32: assembly, change all ENTRY+ENDPROC to SYM_FUNC_* Jiri Slaby
2017-10-02 9:12 ` [PATCH v4 27/27] x86: assembly, replace WEAK uses Jiri Slaby
2017-10-06 15:23 ` [PATCH v4 01/27] linkage: new macros for assembler symbols Josh Poimboeuf
2017-10-25 14:20 ` Jiri Slaby
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=20171002091246.28432-1-jslaby@suse.cz \
--to=jslaby@suse.cz \
--cc=a.p.zijlstra@chello.nl \
--cc=akpm@linux-foundation.org \
--cc=boris.ostrovsky@oracle.com \
--cc=hpa@zytor.com \
--cc=jgross@suse.com \
--cc=jpoimboe@redhat.com \
--cc=len.brown@intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=mingo@kernel.org \
--cc=mingo@redhat.com \
--cc=pavel@ucw.cz \
--cc=rjw@rjwysocki.net \
--cc=tglx@linutronix.de \
--cc=torvalds@linux-foundation.org \
--cc=x86@kernel.org \
--cc=xen-devel@lists.xenproject.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