#include "llvm/Option/ArgList.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/WithColor.h"
enum class LipoAction {
PrintArchs,
VerifyArch,
+ ThinArch,
};
struct Config {
SmallVector<std::string, 1> InputFiles;
SmallVector<std::string, 1> VerifyArchList;
+ std::string ThinArchType;
+ std::string OutputFile;
LipoAction ActionToPerform;
};
} // end namespace
+static void validateArchitectureName(StringRef ArchitectureName) {
+ if (Triple(ArchitectureName).getArch() == Triple::ArchType::UnknownArch)
+ reportError("Invalid architecture: " + ArchitectureName);
+}
+
static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
Config C;
LipoOptTable T;
llvm::opt::InputArgList InputArgs =
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+ if (MissingArgumentCount)
+ reportError("missing argument to " +
+ StringRef(InputArgs.getArgString(MissingArgumentIndex)) +
+ " option");
+
if (InputArgs.size() == 0) {
// PrintHelp does not accept Twine.
T.PrintHelp(errs(), "llvm-lipo input[s] option[s]", "llvm-lipo");
if (C.InputFiles.empty())
reportError("at least one input file should be specified");
+ if (InputArgs.hasArg(LIPO_output))
+ C.OutputFile = InputArgs.getLastArgValue(LIPO_output);
+
SmallVector<opt::Arg *, 1> ActionArgs(InputArgs.filtered(LIPO_action_group));
if (ActionArgs.empty())
reportError("at least one action should be specified");
C.ActionToPerform = LipoAction::PrintArchs;
return C;
+ case LIPO_thin:
+ if (C.InputFiles.size() > 1)
+ reportError("thin expects a single input file");
+ C.ThinArchType = ActionArgs[0]->getValue();
+ validateArchitectureName(C.ThinArchType);
+ if (C.OutputFile.empty())
+ reportError("thin expects a single output file");
+
+ C.ActionToPerform = LipoAction::ThinArch;
+ return C;
+
default:
reportError("llvm-lipo action unspecified");
}
createBinary(InputFile);
if (!BinaryOrErr)
reportError(InputFile, BinaryOrErr.takeError());
- if (!isa<MachOObjectFile>(BinaryOrErr->getBinary()) &&
- !isa<MachOUniversalBinary>(BinaryOrErr->getBinary()))
+ // TODO: Add compatibility for archive files
+ if (BinaryOrErr->getBinary()->isArchive())
+ reportError("File " + InputFile +
+ " is an archive file and is not yet supported.");
+ if (!BinaryOrErr->getBinary()->isMachO() &&
+ !BinaryOrErr->getBinary()->isMachOUniversalBinary())
reportError("File " + InputFile + " has unsupported binary format");
InputBinaries.push_back(std::move(*BinaryOrErr));
}
assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
for (StringRef Arch : VerifyArchList)
- if (Triple(Arch).getArch() == Triple::ArchType::UnknownArch)
- reportError("Invalid architecture: " + Arch);
+ validateArchitectureName(Arch);
if (auto UO =
dyn_cast<MachOUniversalBinary>(InputBinaries.front().getBinary())) {
exit(EXIT_SUCCESS);
}
+LLVM_ATTRIBUTE_NORETURN
+static void extractSlice(ArrayRef<OwningBinary<Binary>> InputBinaries,
+ StringRef ThinArchType, StringRef OutputFileName) {
+ assert(!ThinArchType.empty() && "The architecture type should be non-empty");
+ assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
+ assert(!OutputFileName.empty() && "Thin expects a single output file");
+
+ if (InputBinaries.front().getBinary()->isMachO()) {
+ reportError("input file " +
+ InputBinaries.front().getBinary()->getFileName() +
+ " must be a fat file when the -thin option is specified");
+ exit(EXIT_FAILURE);
+ }
+
+ auto *UO = cast<MachOUniversalBinary>(InputBinaries.front().getBinary());
+ Expected<std::unique_ptr<MachOObjectFile>> Obj =
+ UO->getObjectForArch(ThinArchType);
+ if (!Obj)
+ reportError("fat input file " + UO->getFileName() +
+ " does not contain the specified architecture " + ThinArchType +
+ " to thin it to");
+
+ Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError =
+ FileOutputBuffer::create(OutputFileName,
+ Obj.get()->getMemoryBufferRef().getBufferSize(),
+ sys::fs::can_execute(UO->getFileName())
+ ? FileOutputBuffer::F_executable
+ : 0);
+ if (!OutFileOrError)
+ reportError(OutputFileName, OutFileOrError.takeError());
+ std::copy(Obj.get()->getMemoryBufferRef().getBufferStart(),
+ Obj.get()->getMemoryBufferRef().getBufferEnd(),
+ OutFileOrError.get()->getBufferStart());
+ if (Error E = OutFileOrError.get()->commit())
+ reportError(OutputFileName, std::move(E));
+ exit(EXIT_SUCCESS);
+}
+
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
Config C = parseLipoOptions(makeArrayRef(argv + 1, argc));
case LipoAction::PrintArchs:
printArchs(InputBinaries);
break;
+ case LipoAction::ThinArch:
+ extractSlice(InputBinaries, C.ThinArchType, C.OutputFile);
+ break;
}
return EXIT_SUCCESS;
}