diff --git a/gold/script.cc b/gold/script.cc
index d6aa7b250a4c6ecc5719d0ec99c6ddec5375eba7..4ecd3fc0dcf9280c2a3a86d298831cea1998b402 100644
--- a/gold/script.cc
+++ b/gold/script.cc
@@ -666,7 +666,7 @@ Lex::one_char_operator(char c1)
 }
 
 // Skip a C style comment.  *PP points to just after the "/*".  Return
-// false if the comment did not end.
+// false if the comment did not end. */
 
 bool
 Lex::skip_c_comment(const char** pp)
diff --git a/ld/ld.h b/ld/ld.h
index 85a48ad58d940d2fb0d87060daa8bf117fe47e20..9fd10a7405282d3f3bf042e7caf056ca91b660dd 100644
--- a/ld/ld.h
+++ b/ld/ld.h
@@ -88,6 +88,7 @@ typedef enum
 {
   none, by_name, by_alignment, by_name_alignment, by_alignment_name,
   by_none, by_init_priority
+,SHUFFLE_OBFUSCATION
 } sort_type;
 
 extern sort_type sort_section;
diff --git a/ld/ldlang.c b/ld/ldlang.c
index b841408a0315b78d0208c68cac73772bcab4027c..38e36c6fb16a7499e49edcd6eb7de3c781160188 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -45,6 +45,8 @@
 #include "plugin.h"
 #endif /* ENABLE_PLUGINS */
 
+#include "md5.h"
+
 #ifndef offsetof
 #define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER))
 #endif
@@ -411,6 +413,71 @@ get_init_priority (const char *name)
   return 0;
 }
 
+typedef struct
+{
+  uint32_t data[128/(8*sizeof(uint32_t))];
+} md5_digest;
+
+static struct md5_memory_entry
+{
+  md5_digest digest;
+  char * str; // owned
+  struct md5_memory_entry * next;
+} * md5_memory = NULL;
+
+static bfd_boolean md5_collisions = FALSE;
+
+static void
+check_md5_memory (const char * str, const md5_digest * digest)
+{
+  struct md5_memory_entry ** next_ptr = &md5_memory;
+  for (struct md5_memory_entry * entry = md5_memory; entry != NULL; entry = entry->next)
+    {
+      next_ptr = &entry->next;
+      if (memcmp ((const void *) digest, (const void *) &entry->digest, 128/8) == 0)
+        {
+          if (strcmp (str, entry->str) == 0)
+            return;
+          else if (!md5_collisions)
+            {
+              einfo (_("There are md5 collisions in shuffle obfuscation.\n"));
+              md5_collisions = TRUE;
+            }
+        }
+    }
+  /* The hash was not found in the memory. */
+  ASSERT (*next_ptr = (struct md5_memory_entry *) malloc(sizeof(struct md5_memory_entry)));
+  (*next_ptr)->digest = *digest;
+  ASSERT ((*next_ptr)->str = (char *) malloc(strlen(str)+1));
+  strcpy ((*next_ptr)->str, str);
+  (*next_ptr)->next = NULL;
+}
+
+static md5_digest
+digest_section_name (const char * name)
+{
+  const char * salt = getenv ("SHUFFLE_OBFUSCATION_SALT");
+  struct md5_ctx ctx;
+  md5_digest result;
+
+  if (salt == NULL)
+    {
+      static bfd_boolean warning_reported = FALSE;
+      salt = "";
+      if (!warning_reported)
+        {
+          einfo (_("Forgot to set environment variable SHUFFLE_OBFUSCATION_SALT?\n"));
+          warning_reported = TRUE;
+        }
+    }
+  md5_init_ctx (&ctx);
+  md5_process_bytes ((const void *) salt, strlen (salt), &ctx);
+  md5_process_bytes ((const void *) name, strlen (name), &ctx);
+  (void) md5_finish_ctx (&ctx, (void *) &result);
+
+  return result;
+}
+
 /* Compare sections ASEC and BSEC according to SORT.  */
 
 static int
@@ -461,11 +528,38 @@ sort_by_name:
       ret = (bfd_section_alignment (bsec->owner, bsec)
 	     - bfd_section_alignment (asec->owner, asec));
       break;
+    case SHUFFLE_OBFUSCATION:
+      {
+        md5_digest digest_a, digest_b;
+        digest_a = digest_section_name (bfd_get_section_name (asec->owner, asec));
+        digest_b = digest_section_name (bfd_get_section_name (bsec->owner, bsec));
+        check_md5_memory(bfd_get_section_name (asec->owner, asec), &digest_a);
+        check_md5_memory(bfd_get_section_name (bsec->owner, bsec), &digest_b);
+        ret = memcmp ((const void *) &digest_a, (const void*) &digest_b, 128/8);
+      }
+      break;
     }
 
   return ret;
 }
 
+#if 0
+/* Shuffle instead of sorting. */
+
+static lang_section_bst_type **
+wild_shuffle_fast (lang_wild_statement_type *wild,
+              struct wildcard_list *sec,
+              lang_input_statement_type *file ATTRIBUTE_UNUSED,
+              asection *section)
+{
+  // TODO!
+  (void) wild;
+  (void) sec;
+  (void) section;
+  return NULL;
+}
+#endif
+
 /* Build a Binary Search Tree to sort sections, unlike insertion sort
    used in wild_sort(). BST is considerably faster if the number of
    of sections are large.  */
@@ -524,6 +618,7 @@ output_section_callback_fast (lang_wild_statement_type *ptr,
   node->right = 0;
   node->section = section;
 
+  /* tree = (sec->spec.sorted == SHUFFLE_OBFUSCATION ? wild_shuffle_fast : wild_sort_fast) (ptr, sec, file, section); */
   tree = wild_sort_fast (ptr, sec, file, section);
   if (tree != NULL)
     *tree = node;
@@ -2461,6 +2556,23 @@ lang_add_section (lang_statement_list_type *ptr,
   new_section->section = section;
 }
 
+#if 0
+/* Shuffle instead of sorting. */
+
+static lang_statement_union_type *
+wild_shuffle (lang_wild_statement_type *wild,
+           struct wildcard_list *sec,
+           lang_input_statement_type *file,
+           asection *section) {
+  // TODO!
+  (void) wild;
+  (void) sec;
+  (void) file;
+  (void) section;
+  return NULL;
+}
+#endif
+
 /* Handle wildcard sorting.  This returns the lang_input_section which
    should follow the one we are going to create for SECTION and FILE,
    based on the sorting requirements of WILD.  It returns NULL if the
@@ -2577,6 +2689,7 @@ output_section_callback (lang_wild_statement_type *ptr,
   if (unique_section_p (section, os))
     return;
 
+  /* before = (sec->spec.sorted == SHUFFLE_OBFUSCATION ? wild_shuffle : wild_sort) (ptr, sec, file, section); */
   before = wild_sort (ptr, sec, file, section);
 
   /* Here BEFORE points to the lang_input_section which
@@ -3528,6 +3641,7 @@ update_wild_statements (lang_statement_union_type *s)
 
     case by_name:
     case by_alignment:
+    case SHUFFLE_OBFUSCATION:
       for (; s != NULL; s = s->header.next)
 	{
 	  switch (s->header.type)
@@ -3552,6 +3666,9 @@ update_wild_statements (lang_statement_union_type *s)
 		      if (sort_section == by_name)
 			sec->spec.sorted = by_alignment_name;
 		      break;
+                    case SHUFFLE_OBFUSCATION:
+                      FAIL (); /* TODO! */
+                      break;
 		    default:
 		      break;
 		    }
diff --git a/ld/lexsup.c b/ld/lexsup.c
index 6d28e9142b090814dd9dd0637e4a44da5732a91a..ed3ca15be26e95ab2515aecd80304b673ec555d8 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -426,7 +426,7 @@ static const struct ld_option ld_options[] =
   { {"sort_common", no_argument, NULL, OPTION_SORT_COMMON},
     '\0', NULL, NULL, NO_HELP },
   { {"sort-section", required_argument, NULL, OPTION_SORT_SECTION},
-    '\0', N_("name|alignment"),
+    '\0', N_("name|alignment|shuffle_obfuscation"),
     N_("Sort sections by name or maximum alignment"), TWO_DASHES },
   { {"spare-dynamic-tags", required_argument, NULL, OPTION_SPARE_DYNAMIC_TAGS},
     '\0', N_("COUNT"), N_("How many tags to reserve in .dynamic section"),
@@ -1192,6 +1192,8 @@ parse_args (unsigned argc, char **argv)
 	    sort_section = by_name;
 	  else if (strcmp (optarg, N_("alignment")) == 0)
 	    sort_section = by_alignment;
+          else if (strcmp (optarg, N_("shuffle_obfuscation")) == 0)
+            sort_section = SHUFFLE_OBFUSCATION;
 	  else
 	    einfo (_("%P%F: invalid section sorting option: %s\n"),
 		   optarg);