[llvm-lipo] Implement -thin

Creates thin output file of specified arch_type from the fat input file.

Patch by Anusha Basana <anushabasana@fb.com>

Differential Revision: https://reviews.llvm.org/D63341

llvm-svn: 364107
This commit is contained in:
Shoaib Meenai 2019-06-21 21:59:01 +00:00
parent 3d2837886d
commit e24eeda57d
8 changed files with 489 additions and 5 deletions

View File

@ -0,0 +1,88 @@
--- !mach-o
FileHeader:
magic: 0xFEEDFACE
cputype: 0x00000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 4
sizeofcmds: 312
flags: 0x00002000
LoadCommands:
- cmd: LC_SEGMENT
cmdsize: 192
segname: ''
vmaddr: 0
vmsize: 72
fileoff: 340
filesize: 72
maxprot: 7
initprot: 7
nsects: 2
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
size: 18
offset: 0x00000154
align: 4
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
- sectname: __eh_frame
segname: __TEXT
addr: 0x0000000000000014
size: 52
offset: 0x00000168
align: 2
reloff: 0x00000000
nreloc: 0
flags: 0x6800000B
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
- cmd: LC_VERSION_MIN_MACOSX
cmdsize: 16
version: 656384
sdk: 0
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 412
nsyms: 1
stroff: 424
strsize: 8
- cmd: LC_DYSYMTAB
cmdsize: 80
ilocalsym: 0
nlocalsym: 0
iextdefsym: 0
nextdefsym: 1
iundefsym: 1
nundefsym: 0
tocoff: 0
ntoc: 0
modtaboff: 0
nmodtab: 0
extrefsymoff: 0
nextrefsyms: 0
indirectsymoff: 0
nindirectsyms: 0
extreloff: 0
nextrel: 0
locreloff: 0
nlocrel: 0
LinkEditData:
NameList:
- n_strx: 1
n_type: 0x0F
n_sect: 1
n_desc: 0
n_value: 0
StringTable:
- ''
- _main
- ''
...

View File

@ -0,0 +1,192 @@
--- !fat-mach-o
FatHeader:
magic: 0xCAFEBABE
nfat_arch: 2
FatArchs:
- cputype: 0x00000007
cpusubtype: 0x00000003
offset: 0x0000000000001000
size: 432
align: 12
- cputype: 0x01000007
cpusubtype: 0x00000003
offset: 0x0000000000002000
size: 488
align: 12
Slices:
- !mach-o
FileHeader:
magic: 0xFEEDFACE
cputype: 0x00000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 4
sizeofcmds: 312
flags: 0x00002000
LoadCommands:
- cmd: LC_SEGMENT
cmdsize: 192
segname: ''
vmaddr: 0
vmsize: 72
fileoff: 340
filesize: 72
maxprot: 7
initprot: 7
nsects: 2
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
size: 18
offset: 0x00000154
align: 4
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
- sectname: __eh_frame
segname: __TEXT
addr: 0x0000000000000014
size: 52
offset: 0x00000168
align: 2
reloff: 0x00000000
nreloc: 0
flags: 0x6800000B
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
- cmd: LC_VERSION_MIN_MACOSX
cmdsize: 16
version: 656384
sdk: 0
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 412
nsyms: 1
stroff: 424
strsize: 8
- cmd: LC_DYSYMTAB
cmdsize: 80
ilocalsym: 0
nlocalsym: 0
iextdefsym: 0
nextdefsym: 1
iundefsym: 1
nundefsym: 0
tocoff: 0
ntoc: 0
modtaboff: 0
nmodtab: 0
extrefsymoff: 0
nextrefsyms: 0
indirectsymoff: 0
nindirectsyms: 0
extreloff: 0
nextrel: 0
locreloff: 0
nlocrel: 0
LinkEditData:
NameList:
- n_strx: 1
n_type: 0x0F
n_sect: 1
n_desc: 0
n_value: 0
StringTable:
- ''
- _main
- ''
- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x01000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 4
sizeofcmds: 352
flags: 0x00002000
reserved: 0x00000000
LoadCommands:
- cmd: LC_SEGMENT_64
cmdsize: 232
segname: ''
vmaddr: 0
vmsize: 80
fileoff: 384
filesize: 80
maxprot: 7
initprot: 7
nsects: 2
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
size: 15
offset: 0x00000180
align: 4
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
- sectname: __eh_frame
segname: __TEXT
addr: 0x0000000000000010
size: 64
offset: 0x00000190
align: 3
reloff: 0x00000000
nreloc: 0
flags: 0x6800000B
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
- cmd: LC_VERSION_MIN_MACOSX
cmdsize: 16
version: 656384
sdk: 0
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 464
nsyms: 1
stroff: 480
strsize: 8
- cmd: LC_DYSYMTAB
cmdsize: 80
ilocalsym: 0
nlocalsym: 0
iextdefsym: 0
nextdefsym: 1
iundefsym: 1
nundefsym: 0
tocoff: 0
ntoc: 0
modtaboff: 0
nmodtab: 0
extrefsymoff: 0
nextrefsyms: 0
indirectsymoff: 0
nindirectsyms: 0
extreloff: 0
nextrel: 0
locreloff: 0
nlocrel: 0
LinkEditData:
NameList:
- n_strx: 1
n_type: 0x0F
n_sect: 1
n_desc: 0
n_value: 0
StringTable:
- ''
- _main
- ''
...

View File

@ -9,7 +9,10 @@
# RUN: not llvm-lipo --abcabc 2>&1 | FileCheck --check-prefix=LIPO-UNKNOWN-ARG %s
# RUN: not llvm-lipo %t -archs -verify_arch i386 2>&1 | FileCheck --check-prefix=MULTIPLE_FLAGS %s
# MULTIPLE_FLAGS: only one of the following actions can be specified: -archs -verify_arch
# RUN: not llvm-lipo %t -archs -thin i386 2>&1 | FileCheck --check-prefix=MULTIPLE_FLAGS %s
# RUN: not llvm-lipo %t -thin i386 -verify_arch i386 2>&1 | FileCheck --check-prefix=MULTIPLE_FLAGS %s
# RUN: not llvm-lipo %t -archs -thin i386 -verify_arch i386 2>&1 | FileCheck --check-prefix=MULTIPLE_FLAGS %s
# MULTIPLE_FLAGS: only one of the following actions can be specified:
# LIPO-USAGE: USAGE: llvm-lipo
# LIPO-UNKNOWN-ARG: unknown argument '{{-+}}abcabc'

View File

@ -0,0 +1,50 @@
# Executable testing is not supported on Windows, since all files are considered executable
# UNSUPPORTED: windows
# RUN: yaml2obj %s > %t-universal.o
# RUN: chmod -x %t-universal.o
# RUN: llvm-lipo %t-universal.o -thin i386 -output %t32.o
# RUN: test ! -x %t32.o
# RUN: chmod +x %t-universal.o
# RUN: llvm-lipo %t-universal.o -thin i386 -output %t32-ex.o
# RUN: test -x %t32-ex.o
--- !fat-mach-o
FatHeader:
magic: 0xCAFEBABE
nfat_arch: 2
FatArchs:
- cputype: 0x00000007
cpusubtype: 0x00000003
offset: 0x0000000000001000
size: 28
align: 12
- cputype: 0x01000007
cpusubtype: 0x00000003
offset: 0x0000000000002000
size: 32
align: 12
Slices:
- !mach-o
FileHeader:
magic: 0xFEEDFACE
cputype: 0x00000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 0
sizeofcmds: 0
flags: 0x00002000
- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x01000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 0
sizeofcmds: 0
flags: 0x00002000
reserved: 0x00000000
...

View File

@ -0,0 +1,21 @@
# RUN: yaml2obj %s > %t
# RUN: not llvm-lipo %t -thin i386 2>&1 | FileCheck --check-prefix=NO_OUTPUT %s
# NO_OUTPUT: error: thin expects a single output file
# RUN: not llvm-lipo %t %t -thin i386 2>&1 | FileCheck --check-prefix=MULTIPLE_INPUT_OBJ %s
# MULTIPLE_INPUT_OBJ: thin expects a single input file
# RUN: not llvm-lipo %t -thin i386 -output %t.out 2>&1 | FileCheck --check-prefix=INPUT_FILE_THIN %s
# INPUT_FILE_THIN: must be a fat file when the -thin option is specified
--- !mach-o
FileHeader:
magic: 0xFEEDFACE
cputype: 0x00000097
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 0
sizeofcmds: 0
flags: 0x00002000
...

View File

@ -0,0 +1,49 @@
# RUN: yaml2obj %s > %t
# RUN: not llvm-lipo %t -thin arc -output %t.out 2>&1 | FileCheck --check-prefix=ARCH_NOT_IN_FILE %s
# ARCH_NOT_IN_FILE: does not contain the specified architecture arc to thin it to
# RUN: not llvm-lipo %t -thin aarch101 -output %t.out 2>&1 | FileCheck --check-prefix=INVALID_ARCH %s
# INVALID_ARCH: Invalid architecture: aarch101
# RUN: yaml2obj %p/Inputs/i386-x86_64-universal.yaml > %t-universal.o
# RUN: llvm-lipo %t-universal.o -thin i386 -output %t32.o
# RUN: yaml2obj %p/Inputs/i386-slice.yaml > %t-basic32.o
# RUN: cmp %t32.o %t-basic32.o
--- !fat-mach-o
FatHeader:
magic: 0xCAFEBABE
nfat_arch: 2
FatArchs:
- cputype: 0x00000007
cpusubtype: 0x00000003
offset: 0x0000000000001000
size: 28
align: 12
- cputype: 0x01000007
cpusubtype: 0x00000003
offset: 0x0000000000002000
size: 32
align: 12
Slices:
- !mach-o
FileHeader:
magic: 0xFEEDFACE
cputype: 0x00000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 0
sizeofcmds: 0
flags: 0x00002000
- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x01000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 0
sizeofcmds: 0
flags: 0x00002000
reserved: 0x00000000
...

View File

@ -17,3 +17,12 @@ def verify_arch
def archs : Option<["-", "--"], "archs", KIND_FLAG>,
Group<action_group>,
HelpText<"Display the arch_types present in the input file">;
def thin : Option<["-", "--"], "thin", KIND_SEPARATE>,
Group<action_group>,
HelpText<"Create a thin output file of specified arch_type from the "
"fat input file. Requires -output option">;
def output : Option<["-", "--"], "output", KIND_SEPARATE>,
HelpText<"Create output file with specified name">;
def o : JoinedOrSeparate<["-"], "o">, Alias<output>;

View File

@ -19,6 +19,7 @@
#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"
@ -78,16 +79,24 @@ public:
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;
@ -95,6 +104,11 @@ static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
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");
@ -121,6 +135,9 @@ static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
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");
@ -151,6 +168,17 @@ static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
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");
}
@ -164,8 +192,12 @@ readInputBinaries(ArrayRef<std::string> InputFiles) {
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));
}
@ -180,8 +212,7 @@ static void verifyArch(ArrayRef<OwningBinary<Binary>> InputBinaries,
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())) {
@ -238,6 +269,44 @@ static void printArchs(ArrayRef<OwningBinary<Binary>> InputBinaries) {
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));
@ -251,6 +320,9 @@ int main(int argc, char **argv) {
case LipoAction::PrintArchs:
printArchs(InputBinaries);
break;
case LipoAction::ThinArch:
extractSlice(InputBinaries, C.ThinArchType, C.OutputFile);
break;
}
return EXIT_SUCCESS;
}