1 //===- llvm-objcopy.cpp ---------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm-objcopy.h" 10 #include "Buffer.h" 11 #include "CopyConfig.h" 12 #include "ELF/ELFObjcopy.h" 13 #include "COFF/COFFObjcopy.h" 14 #include "MachO/MachOObjcopy.h" 15 16 #include "llvm/ADT/STLExtras.h" 17 #include "llvm/ADT/SmallVector.h" 18 #include "llvm/ADT/StringRef.h" 19 #include "llvm/ADT/Twine.h" 20 #include "llvm/Object/Archive.h" 21 #include "llvm/Object/ArchiveWriter.h" 22 #include "llvm/Object/Binary.h" 23 #include "llvm/Object/COFF.h" 24 #include "llvm/Object/ELFObjectFile.h" 25 #include "llvm/Object/ELFTypes.h" 26 #include "llvm/Object/Error.h" 27 #include "llvm/Object/MachO.h" 28 #include "llvm/Option/Arg.h" 29 #include "llvm/Option/ArgList.h" 30 #include "llvm/Option/Option.h" 31 #include "llvm/Support/Casting.h" 32 #include "llvm/Support/Error.h" 33 #include "llvm/Support/ErrorHandling.h" 34 #include "llvm/Support/ErrorOr.h" 35 #include "llvm/Support/InitLLVM.h" 36 #include "llvm/Support/Memory.h" 37 #include "llvm/Support/Path.h" 38 #include "llvm/Support/Process.h" 39 #include "llvm/Support/WithColor.h" 40 #include "llvm/Support/raw_ostream.h" 41 #include <algorithm> 42 #include <cassert> 43 #include <cstdlib> 44 #include <memory> 45 #include <string> 46 #include <system_error> 47 #include <utility> 48 49 namespace llvm { 50 namespace objcopy { 51 52 // The name this program was invoked as. 53 StringRef ToolName; 54 55 LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { 56 WithColor::error(errs(), ToolName) << Message << "\n"; 57 exit(1); 58 } 59 60 LLVM_ATTRIBUTE_NORETURN void error(Error E) { 61 assert(E); 62 std::string Buf; 63 raw_string_ostream OS(Buf); 64 logAllUnhandledErrors(std::move(E), OS); 65 OS.flush(); 66 WithColor::error(errs(), ToolName) << Buf; 67 exit(1); 68 } 69 70 LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) { 71 assert(EC); 72 error(createFileError(File, EC)); 73 } 74 75 LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { 76 assert(E); 77 std::string Buf; 78 raw_string_ostream OS(Buf); 79 logAllUnhandledErrors(std::move(E), OS); 80 OS.flush(); 81 WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; 82 exit(1); 83 } 84 85 ErrorSuccess reportWarning(Error E) { 86 assert(E); 87 WithColor::warning(errs(), ToolName) << toString(std::move(E)); 88 return Error::success(); 89 } 90 91 } // end namespace objcopy 92 } // end namespace llvm 93 94 using namespace llvm; 95 using namespace llvm::object; 96 using namespace llvm::objcopy; 97 98 // For regular archives this function simply calls llvm::writeArchive, 99 // For thin archives it writes the archive file itself as well as its members. 100 static Error deepWriteArchive(StringRef ArcName, 101 ArrayRef<NewArchiveMember> NewMembers, 102 bool WriteSymtab, object::Archive::Kind Kind, 103 bool Deterministic, bool Thin) { 104 if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind, 105 Deterministic, Thin)) 106 return createFileError(ArcName, std::move(E)); 107 108 if (!Thin) 109 return Error::success(); 110 111 for (const NewArchiveMember &Member : NewMembers) { 112 // Internally, FileBuffer will use the buffer created by 113 // FileOutputBuffer::create, for regular files (that is the case for 114 // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer. 115 // OnDiskBuffer uses a temporary file and then renames it. So in reality 116 // there is no inefficiency / duplicated in-memory buffers in this case. For 117 // now in-memory buffers can not be completely avoided since 118 // NewArchiveMember still requires them even though writeArchive does not 119 // write them on disk. 120 FileBuffer FB(Member.MemberName); 121 if (Error E = FB.allocate(Member.Buf->getBufferSize())) 122 return E; 123 std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(), 124 FB.getBufferStart()); 125 if (Error E = FB.commit()) 126 return E; 127 } 128 return Error::success(); 129 } 130 131 /// The function executeObjcopyOnIHex does the dispatch based on the format 132 /// of the output specified by the command line options. 133 static Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, 134 Buffer &Out) { 135 // TODO: support output formats other than ELF. 136 return elf::executeObjcopyOnIHex(Config, In, Out); 137 } 138 139 /// The function executeObjcopyOnRawBinary does the dispatch based on the format 140 /// of the output specified by the command line options. 141 static Error executeObjcopyOnRawBinary(const CopyConfig &Config, 142 MemoryBuffer &In, Buffer &Out) { 143 switch (Config.OutputFormat) { 144 case FileFormat::ELF: 145 // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the 146 // output format is binary/ihex or it's not given. This behavior differs from 147 // GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details. 148 case FileFormat::Binary: 149 case FileFormat::IHex: 150 case FileFormat::Unspecified: 151 return elf::executeObjcopyOnRawBinary(Config, In, Out); 152 } 153 154 llvm_unreachable("unsupported output format"); 155 } 156 157 /// The function executeObjcopyOnBinary does the dispatch based on the format 158 /// of the input binary (ELF, MachO or COFF). 159 static Error executeObjcopyOnBinary(const CopyConfig &Config, 160 object::Binary &In, Buffer &Out) { 161 if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) 162 return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); 163 else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) 164 return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); 165 else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) 166 return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); 167 else 168 return createStringError(object_error::invalid_file_type, 169 "unsupported object file format"); 170 } 171 172 static Error executeObjcopyOnArchive(const CopyConfig &Config, 173 const Archive &Ar) { 174 std::vector<NewArchiveMember> NewArchiveMembers; 175 Error Err = Error::success(); 176 for (const Archive::Child &Child : Ar.children(Err)) { 177 Expected<StringRef> ChildNameOrErr = Child.getName(); 178 if (!ChildNameOrErr) 179 return createFileError(Ar.getFileName(), ChildNameOrErr.takeError()); 180 181 Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); 182 if (!ChildOrErr) 183 return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", 184 ChildOrErr.takeError()); 185 186 MemBuffer MB(ChildNameOrErr.get()); 187 if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB)) 188 return E; 189 190 Expected<NewArchiveMember> Member = 191 NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); 192 if (!Member) 193 return createFileError(Ar.getFileName(), Member.takeError()); 194 Member->Buf = MB.releaseMemoryBuffer(); 195 Member->MemberName = Member->Buf->getBufferIdentifier(); 196 NewArchiveMembers.push_back(std::move(*Member)); 197 } 198 if (Err) 199 return createFileError(Config.InputFilename, std::move(Err)); 200 201 return deepWriteArchive(Config.OutputFilename, NewArchiveMembers, 202 Ar.hasSymbolTable(), Ar.kind(), 203 Config.DeterministicArchives, Ar.isThin()); 204 } 205 206 static Error restoreStatOnFile(StringRef Filename, 207 const sys::fs::file_status &Stat, 208 bool PreserveDates) { 209 int FD; 210 211 // Writing to stdout should not be treated as an error here, just 212 // do not set access/modification times or permissions. 213 if (Filename == "-") 214 return Error::success(); 215 216 if (auto EC = 217 sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) 218 return createFileError(Filename, EC); 219 220 if (PreserveDates) 221 if (auto EC = sys::fs::setLastAccessAndModificationTime( 222 FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) 223 return createFileError(Filename, EC); 224 225 sys::fs::file_status OStat; 226 if (std::error_code EC = sys::fs::status(FD, OStat)) 227 return createFileError(Filename, EC); 228 if (OStat.type() == sys::fs::file_type::regular_file) 229 #ifdef _WIN32 230 if (auto EC = sys::fs::setPermissions( 231 Filename, static_cast<sys::fs::perms>(Stat.permissions() & 232 ~sys::fs::getUmask()))) 233 #else 234 if (auto EC = sys::fs::setPermissions( 235 FD, static_cast<sys::fs::perms>(Stat.permissions() & 236 ~sys::fs::getUmask()))) 237 #endif 238 return createFileError(Filename, EC); 239 240 if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) 241 return createFileError(Filename, EC); 242 243 return Error::success(); 244 } 245 246 /// The function executeObjcopy does the higher level dispatch based on the type 247 /// of input (raw binary, archive or single object file) and takes care of the 248 /// format-agnostic modifications, i.e. preserving dates. 249 static Error executeObjcopy(const CopyConfig &Config) { 250 sys::fs::file_status Stat; 251 if (Config.InputFilename != "-") { 252 if (auto EC = sys::fs::status(Config.InputFilename, Stat)) 253 return createFileError(Config.InputFilename, EC); 254 } else { 255 Stat.permissions(static_cast<sys::fs::perms>(0777)); 256 } 257 258 typedef Error (*ProcessRawFn)(const CopyConfig &, MemoryBuffer &, Buffer &); 259 ProcessRawFn ProcessRaw; 260 switch (Config.InputFormat) { 261 case FileFormat::Binary: 262 ProcessRaw = executeObjcopyOnRawBinary; 263 break; 264 case FileFormat::IHex: 265 ProcessRaw = executeObjcopyOnIHex; 266 break; 267 default: 268 ProcessRaw = nullptr; 269 } 270 271 if (ProcessRaw) { 272 auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); 273 if (!BufOrErr) 274 return createFileError(Config.InputFilename, BufOrErr.getError()); 275 FileBuffer FB(Config.OutputFilename); 276 if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB)) 277 return E; 278 } else { 279 Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = 280 createBinary(Config.InputFilename); 281 if (!BinaryOrErr) 282 return createFileError(Config.InputFilename, BinaryOrErr.takeError()); 283 284 if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { 285 if (Error E = executeObjcopyOnArchive(Config, *Ar)) 286 return E; 287 } else { 288 FileBuffer FB(Config.OutputFilename); 289 if (Error E = executeObjcopyOnBinary(Config, 290 *BinaryOrErr.get().getBinary(), FB)) 291 return E; 292 } 293 } 294 295 if (Error E = 296 restoreStatOnFile(Config.OutputFilename, Stat, Config.PreserveDates)) 297 return E; 298 299 if (!Config.SplitDWO.empty()) { 300 Stat.permissions(static_cast<sys::fs::perms>(0666)); 301 if (Error E = 302 restoreStatOnFile(Config.SplitDWO, Stat, Config.PreserveDates)) 303 return E; 304 } 305 306 return Error::success(); 307 } 308 309 int main(int argc, char **argv) { 310 InitLLVM X(argc, argv); 311 ToolName = argv[0]; 312 bool IsStrip = sys::path::stem(ToolName).contains("strip"); 313 Expected<DriverConfig> DriverConfig = 314 IsStrip ? parseStripOptions(makeArrayRef(argv + 1, argc), reportWarning) 315 : parseObjcopyOptions(makeArrayRef(argv + 1, argc)); 316 if (!DriverConfig) { 317 logAllUnhandledErrors(DriverConfig.takeError(), 318 WithColor::error(errs(), ToolName)); 319 return 1; 320 } 321 for (const CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { 322 if (Error E = executeObjcopy(CopyConfig)) { 323 logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); 324 return 1; 325 } 326 } 327 328 return 0; 329 } 330