1 //===- llvm-objcopy.cpp ---------------------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "llvm-objcopy.h" 11 #include "Object.h" 12 #include "llvm/ADT/STLExtras.h" 13 #include "llvm/ADT/StringRef.h" 14 #include "llvm/ADT/Twine.h" 15 #include "llvm/BinaryFormat/ELF.h" 16 #include "llvm/Object/Binary.h" 17 #include "llvm/Object/ELFObjectFile.h" 18 #include "llvm/Object/ELFTypes.h" 19 #include "llvm/Object/Error.h" 20 #include "llvm/Support/Casting.h" 21 #include "llvm/Support/CommandLine.h" 22 #include "llvm/Support/Compiler.h" 23 #include "llvm/Support/Error.h" 24 #include "llvm/Support/ErrorHandling.h" 25 #include "llvm/Support/ErrorOr.h" 26 #include "llvm/Support/FileOutputBuffer.h" 27 #include "llvm/Support/ManagedStatic.h" 28 #include "llvm/Support/PrettyStackTrace.h" 29 #include "llvm/Support/Signals.h" 30 #include "llvm/Support/raw_ostream.h" 31 #include <algorithm> 32 #include <cassert> 33 #include <cstdlib> 34 #include <functional> 35 #include <iterator> 36 #include <memory> 37 #include <string> 38 #include <system_error> 39 #include <utility> 40 41 using namespace llvm; 42 using namespace object; 43 using namespace ELF; 44 45 // The name this program was invoked as. 46 static StringRef ToolName; 47 48 namespace llvm { 49 50 LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { 51 errs() << ToolName << ": " << Message << ".\n"; 52 errs().flush(); 53 exit(1); 54 } 55 56 LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) { 57 assert(EC); 58 errs() << ToolName << ": '" << File << "': " << EC.message() << ".\n"; 59 exit(1); 60 } 61 62 LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { 63 assert(E); 64 std::string Buf; 65 raw_string_ostream OS(Buf); 66 logAllUnhandledErrors(std::move(E), OS, ""); 67 OS.flush(); 68 errs() << ToolName << ": '" << File << "': " << Buf; 69 exit(1); 70 } 71 72 } // end namespace llvm 73 74 static cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input>")); 75 static cl::opt<std::string> OutputFilename(cl::Positional, cl::desc("<output>"), 76 cl::init("-")); 77 static cl::opt<std::string> 78 OutputFormat("O", cl::desc("Set output format to one of the following:" 79 "\n\tbinary")); 80 static cl::list<std::string> ToRemove("remove-section", 81 cl::desc("Remove <section>"), 82 cl::value_desc("section")); 83 static cl::alias ToRemoveA("R", cl::desc("Alias for remove-section"), 84 cl::aliasopt(ToRemove)); 85 static cl::opt<bool> StripAll( 86 "strip-all", 87 cl::desc( 88 "Removes non-allocated sections other than .gnu.warning* sections")); 89 static cl::opt<bool> 90 StripAllGNU("strip-all-gnu", 91 cl::desc("Removes symbol, relocation, and debug information")); 92 static cl::opt<bool> StripDebug("strip-debug", 93 cl::desc("Removes all debug information")); 94 static cl::opt<bool> StripSections("strip-sections", 95 cl::desc("Remove all section headers")); 96 static cl::opt<bool> StripNonAlloc("strip-non-alloc", 97 cl::desc("Remove all non-allocated sections")); 98 static cl::opt<bool> 99 StripDWO("strip-dwo", cl::desc("Remove all DWARF .dwo sections from file")); 100 static cl::opt<bool> ExtractDWO( 101 "extract-dwo", 102 cl::desc("Remove all sections that are not DWARF .dwo sections from file")); 103 static cl::opt<std::string> 104 SplitDWO("split-dwo", 105 cl::desc("Equivalent to extract-dwo on the input file to " 106 "<dwo-file>, then strip-dwo on the input file"), 107 cl::value_desc("dwo-file")); 108 109 using SectionPred = std::function<bool(const SectionBase &Sec)>; 110 111 bool IsDWOSection(const SectionBase &Sec) { 112 return Sec.Name.endswith(".dwo"); 113 } 114 115 template <class ELFT> 116 bool OnlyKeepDWOPred(const Object<ELFT> &Obj, const SectionBase &Sec) { 117 // We can't remove the section header string table. 118 if (&Sec == Obj.getSectionHeaderStrTab()) 119 return false; 120 // Short of keeping the string table we want to keep everything that is a DWO 121 // section and remove everything else. 122 return !IsDWOSection(Sec); 123 } 124 125 template <class ELFT> 126 void WriteObjectFile(const Object<ELFT> &Obj, StringRef File) { 127 std::unique_ptr<FileOutputBuffer> Buffer; 128 Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = 129 FileOutputBuffer::create(File, Obj.totalSize(), 130 FileOutputBuffer::F_executable); 131 handleAllErrors(BufferOrErr.takeError(), [](const ErrorInfoBase &) { 132 error("failed to open " + OutputFilename); 133 }); 134 Buffer = std::move(*BufferOrErr); 135 136 Obj.write(*Buffer); 137 if (auto E = Buffer->commit()) 138 reportError(File, errorToErrorCode(std::move(E))); 139 } 140 141 template <class ELFT> 142 void SplitDWOToFile(const ELFObjectFile<ELFT> &ObjFile, StringRef File) { 143 // Construct a second output file for the DWO sections. 144 ELFObject<ELFT> DWOFile(ObjFile); 145 146 DWOFile.removeSections([&](const SectionBase &Sec) { 147 return OnlyKeepDWOPred<ELFT>(DWOFile, Sec); 148 }); 149 DWOFile.finalize(); 150 WriteObjectFile(DWOFile, File); 151 } 152 153 template <class ELFT> 154 void CopyBinary(const ELFObjectFile<ELFT> &ObjFile) { 155 std::unique_ptr<Object<ELFT>> Obj; 156 157 if (!OutputFormat.empty() && OutputFormat != "binary") 158 error("invalid output format '" + OutputFormat + "'"); 159 if (!OutputFormat.empty() && OutputFormat == "binary") 160 Obj = llvm::make_unique<BinaryObject<ELFT>>(ObjFile); 161 else 162 Obj = llvm::make_unique<ELFObject<ELFT>>(ObjFile); 163 164 if (!SplitDWO.empty()) 165 SplitDWOToFile<ELFT>(ObjFile, SplitDWO.getValue()); 166 167 SectionPred RemovePred = [](const SectionBase &) { return false; }; 168 169 if (!ToRemove.empty()) { 170 RemovePred = [&](const SectionBase &Sec) { 171 return std::find(std::begin(ToRemove), std::end(ToRemove), Sec.Name) != 172 std::end(ToRemove); 173 }; 174 } 175 176 if (StripDWO || !SplitDWO.empty()) 177 RemovePred = [RemovePred](const SectionBase &Sec) { 178 return IsDWOSection(Sec) || RemovePred(Sec); 179 }; 180 181 if (ExtractDWO) 182 RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { 183 return OnlyKeepDWOPred(*Obj, Sec) || RemovePred(Sec); 184 }; 185 186 if (StripAllGNU) 187 RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { 188 if (RemovePred(Sec)) 189 return true; 190 if ((Sec.Flags & SHF_ALLOC) != 0) 191 return false; 192 if (&Sec == Obj->getSectionHeaderStrTab()) 193 return false; 194 switch(Sec.Type) { 195 case SHT_SYMTAB: 196 case SHT_REL: 197 case SHT_RELA: 198 case SHT_STRTAB: 199 return true; 200 } 201 return Sec.Name.startswith(".debug"); 202 }; 203 204 if (StripSections) { 205 RemovePred = [RemovePred](const SectionBase &Sec) { 206 return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0; 207 }; 208 Obj->WriteSectionHeaders = false; 209 } 210 211 if (StripDebug) { 212 RemovePred = [RemovePred](const SectionBase &Sec) { 213 return RemovePred(Sec) || Sec.Name.startswith(".debug"); 214 }; 215 } 216 217 if (StripNonAlloc) 218 RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { 219 if (RemovePred(Sec)) 220 return true; 221 if (&Sec == Obj->getSectionHeaderStrTab()) 222 return false; 223 return (Sec.Flags & SHF_ALLOC) == 0; 224 }; 225 226 if (StripAll) 227 RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { 228 if (RemovePred(Sec)) 229 return true; 230 if (&Sec == Obj->getSectionHeaderStrTab()) 231 return false; 232 if (Sec.Name.startswith(".gnu.warning")) 233 return false; 234 return (Sec.Flags & SHF_ALLOC) == 0; 235 }; 236 237 Obj->removeSections(RemovePred); 238 Obj->finalize(); 239 WriteObjectFile(*Obj, OutputFilename.getValue()); 240 } 241 242 int main(int argc, char **argv) { 243 // Print a stack trace if we signal out. 244 sys::PrintStackTraceOnErrorSignal(argv[0]); 245 PrettyStackTraceProgram X(argc, argv); 246 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 247 cl::ParseCommandLineOptions(argc, argv, "llvm objcopy utility\n"); 248 ToolName = argv[0]; 249 if (InputFilename.empty()) { 250 cl::PrintHelpMessage(); 251 return 2; 252 } 253 Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(InputFilename); 254 if (!BinaryOrErr) 255 reportError(InputFilename, BinaryOrErr.takeError()); 256 Binary &Binary = *BinaryOrErr.get().getBinary(); 257 if (auto *o = dyn_cast<ELFObjectFile<ELF64LE>>(&Binary)) { 258 CopyBinary(*o); 259 return 0; 260 } 261 if (auto *o = dyn_cast<ELFObjectFile<ELF32LE>>(&Binary)) { 262 CopyBinary(*o); 263 return 0; 264 } 265 if (auto *o = dyn_cast<ELFObjectFile<ELF64BE>>(&Binary)) { 266 CopyBinary(*o); 267 return 0; 268 } 269 if (auto *o = dyn_cast<ELFObjectFile<ELF32BE>>(&Binary)) { 270 CopyBinary(*o); 271 return 0; 272 } 273 reportError(InputFilename, object_error::invalid_file_type); 274 } 275