Index: test/tools/llvm-objcopy/drawf-fission.test
===================================================================
--- /dev/null
+++ test/tools/llvm-objcopy/drawf-fission.test
@@ -0,0 +1,39 @@
+# RUN: llvm-objcopy -extract-dwo %p/Inputs/dwarf.dwo %t
+# RUN: llvm-objcopy -strip-dwo %p/Inputs/dwarf.dwo %t2
+# RUN: llvm-objcopy -gsplit-dwarf=%t3 %p/Inputs/dwarf.dwo %t4
+# RUN: llvm-readobj -sections %t | FileCheck %s -check-prefix=DWARF
+# RUN: llvm-readobj -sections %t2 | FileCheck %s -check-prefix=STRIP
+# RUN: diff %t %t3
+# RUN: diff %t2 %t4
+
+#DWARF:     Name: .debug_loc.dwo
+#DWARF:     Name: .debug_str.dwo
+#DWARF:     Name: .debug_str_offsets.dwo
+#DWARF:     Name: .debug_info.dwo
+#DWARF:     Name: .debug_abbrev.dwo
+#DWARF:     Name: .debug_line.dwo
+#DWARF:     Name: .strtab
+
+#STRIP:    Name: .text
+#STRIP:    Name: .rodata.str1.1
+#STRIP:    Name: .debug_str
+#STRIP:    Name: .debug_abbrev
+#STRIP:    Name: .debug_info
+#STRIP:    Name: .debug_ranges
+#STRIP:    Name: .debug_macinfo
+#STRIP:    Name: .debug_addr
+#STRIP:    Name: .debug_pubnames
+#STRIP:    Name: .debug_pubtypes
+#STRIP:    Name: .comment
+#STRIP:    Name: .note.GNU-stack
+#STRIP:    Name: .debug_frame
+#STRIP:    Name: .debug_line
+#STRIP:    Name: .symtab
+#STRIP:    Name: .rela.text
+#STRIP:    Name: .rela.debug_info
+#STRIP:    Name: .rela.debug_addr
+#STRIP:    Name: .rela.debug_pubnames
+#STRIP:    Name: .rela.debug_pubtypes
+#STRIP:    Name: .rela.debug_frame
+#STRIP:    Name: .rela.debug_line
+#STRIP:    Name: .strtab
Index: tools/llvm-objcopy/Object.h
===================================================================
--- tools/llvm-objcopy/Object.h
+++ tools/llvm-objcopy/Object.h
@@ -349,6 +349,7 @@
   bool WriteSectionHeaders = true;
 
   Object(const llvm::object::ELFObjectFile<ELFT> &Obj);
+  const SectionBase *getSectionHeaderStrTab() const { return SectionNames; }
   void removeSections(std::function<bool(const SectionBase &)> ToRemove);
   virtual size_t totalSize() const = 0;
   virtual void finalize() = 0;
Index: tools/llvm-objcopy/llvm-objcopy.cpp
===================================================================
--- tools/llvm-objcopy/llvm-objcopy.cpp
+++ tools/llvm-objcopy/llvm-objcopy.cpp
@@ -62,12 +62,64 @@
                     cl::aliasopt(ToRemove));
 cl::opt<bool> StripSections("strip-sections",
                             cl::desc("Remove all section headers"));
+cl::opt<bool> StripDwo("strip-dwo",
+                       cl::desc("Remove all DWARF info from file"));
+cl::opt<bool> ExtractDwo("extract-dwo",
+                         cl::desc("Remove all non-DWARF info from file"));
+cl::opt<std::string>
+    GSplitDwarf("gsplit-dwarf",
+                cl::desc("equivalent to strip-dwo and then extract-dwo on "
+                         "the gsplit-dwarf file"));
+
+template <class ELFT>
+bool StripDwoPred(const Object<ELFT> &Obj, const SectionBase &Sec) {
+  // We just simply remove the .dwo sections. That's it! This has the
+  // consequence of removing relocations for .dwo sections and symbols defined
+  // in .dwo sections.
+  return Sec.Name.endswith(".dwo");
+}
+
+template <class ELFT>
+bool OnlyKeepDwoPred(const Object<ELFT> &Obj, const SectionBase &Sec) {
+  // We can't remove the section header string table.
+  if (&Sec == Obj.getSectionHeaderStrTab())
+    return false;
+  // Otherwise we just want to keep what we removed in the other file.
+  return !StripDwoPred(Obj, Sec);
+}
+
+template <class ELFT>
+void WriteObjectFile(const Object<ELFT> &Obj, StringRef File) {
+  std::unique_ptr<FileOutputBuffer> Buffer;
+  ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+      FileOutputBuffer::create(File, Obj.totalSize(),
+                               FileOutputBuffer::F_executable);
+  if (BufferOrErr.getError())
+    error("failed to open " + OutputFilename);
+  else
+    Buffer = std::move(*BufferOrErr);
+  Obj.write(*Buffer);
+  if (auto EC = Buffer->commit())
+    reportError(File, EC);
+}
+
+template <class ELFT>
+void SplitDWOToFile(const ELFObjectFile<ELFT> &ObjFile, StringRef File) {
+  // Construct a second output file for the DWO sections.
+  ELFObject<ELFT> GSplitObj(ObjFile);
+
+  GSplitObj.removeSections([&](const SectionBase &Sec) {
+    return OnlyKeepDwoPred<ELFT>(GSplitObj, Sec);
+  });
+  GSplitObj.finalize();
+  WriteObjectFile(GSplitObj, File);
+}
 
 typedef std::function<bool(const SectionBase &Sec)> SectionPred;
 
 void CopyBinary(const ELFObjectFile<ELF64LE> &ObjFile) {
-  std::unique_ptr<FileOutputBuffer> Buffer;
   std::unique_ptr<Object<ELF64LE>> Obj;
+
   if (!OutputFormat.empty() && OutputFormat != "binary")
     error("invalid output format '" + OutputFormat + "'");
   if (!OutputFormat.empty() && OutputFormat == "binary")
@@ -84,6 +136,19 @@
     };
   }
 
+  if (StripDwo || !GSplitDwarf.empty())
+    RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
+      return StripDwoPred(*Obj, Sec) || RemovePred(Sec);
+    };
+
+  if (ExtractDwo)
+    RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
+      return OnlyKeepDwoPred(*Obj, Sec) || RemovePred(Sec);
+    };
+
+  if (!GSplitDwarf.empty())
+    SplitDWOToFile<ELF64LE>(ObjFile, GSplitDwarf.getValue());
+
   if (StripSections) {
     RemovePred = [RemovePred](const SectionBase &Sec) {
       return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0;
@@ -92,21 +157,8 @@
   }
 
   Obj->removeSections(RemovePred);
-
   Obj->finalize();
-  ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
-      FileOutputBuffer::create(OutputFilename, Obj->totalSize(),
-                               FileOutputBuffer::F_executable);
-  if (BufferOrErr.getError())
-    error("failed to open " + OutputFilename);
-  else
-    Buffer = std::move(*BufferOrErr);
-  std::error_code EC;
-  if (EC)
-    report_fatal_error(EC.message());
-  Obj->write(*Buffer);
-  if (auto EC = Buffer->commit())
-    reportError(OutputFilename, EC);
+  WriteObjectFile(*Obj, OutputFilename.getValue());
 }
 
 int main(int argc, char **argv) {