[llvm-libtool-darwin] Add support for -arch_only

Add support for -arch_only option for llvm-libtool-darwin. This diff
also adds support for accepting universal files as input and flattening
them to create the required static library. Supports input universal
files contaning both Mach-O object files or archives.

Differences from cctools' libtool:
- `-arch_only` can be specified multiple times
- archives containing universal files are considered invalid (libtool
allows such archives)

Reviewed by jhenderson, smeenai

Differential Revision: https://reviews.llvm.org/D84770
This commit is contained in:
Sameer Arora 2020-07-28 09:50:51 -07:00
parent 05169af5ce
commit bd2853f799
6 changed files with 705 additions and 55 deletions

View File

@ -21,6 +21,26 @@ OPTIONS
-------- --------
:program:`llvm-libtool-darwin` supports the following options: :program:`llvm-libtool-darwin` supports the following options:
.. option:: -arch_only <architecture>
Build a static library only for the specified `<architecture>` and ignore all
other architectures in the files.
.. option:: -color
Use colors in output.
.. option:: -D
Use zero for timestamps and UIDs/GIDs. This is set by default.
.. option:: -filelist <listfile[,dirname]>
Read input file names from `<listfile>`. File names are specified in `<listfile>`
one per line, separated only by newlines. Whitespace on a line is assumed
to be part of the filename. If the directory name, `dirname`, is also
specified then it is prepended to each file name in the `<listfile>`.
.. option:: -h, -help .. option:: -h, -help
Show help and usage for this command. Show help and usage for this command.
@ -30,36 +50,21 @@ OPTIONS
Show help and usage for this command without grouping the options Show help and usage for this command without grouping the options
into categories. into categories.
.. option:: -color
Use colors in output.
.. option:: -version
Display the version of this program.
.. option:: -D
Use zero for timestamps and UIDs/GIDs. This is set by default.
.. option:: -U
Use actual timestamps and UIDs/GIDs.
.. option:: -o <filename> .. option:: -o <filename>
Specify the output file name. Must be specified exactly once. Specify the output file name. Must be specified exactly once.
.. option:: -static .. option:: -static
Produces a static library from the input files. Produces a static library from the input files.
.. option:: -filelist <listfile[,dirname]> .. option:: -U
Read input file names from `<listfile>`. File names are specified in `<listfile>` Use actual timestamps and UIDs/GIDs.
one per line, separated only by newlines. Whitespace on a line is assumed
to be part of the filename. If the directory name, `dirname`, is also .. option:: -version
specified then it is prepended to each file name in the `<listfile>`.
Display the version of this program.
EXIT STATUS EXIT STATUS
----------- -----------

View File

@ -0,0 +1,269 @@
## This test checks that the CPU subtype matching logic is handled correctly.
# RUN: yaml2obj %s --docnum=1 -o %t.armv6
# RUN: yaml2obj %s --docnum=2 -o %t.armv7
# RUN: llvm-libtool-darwin -static -o %t.lib %t.armv6 %t.armv7 -arch_only armv7
## Check that only armv7 binary is present:
# RUN: llvm-ar t %t.lib | \
# RUN: FileCheck %s --check-prefix=ARM-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp
# ARM-NAMES: [[PREFIX]].armv7
## Check that only armv7 symbol is present:
# RUN: llvm-nm --print-armap %t.lib | \
# RUN: FileCheck %s --check-prefix=ARM-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines
# ARM-SYMBOLS: Archive map
# ARM-SYMBOLS-NEXT: _armv7 in [[PREFIX]].armv7
# ARM-SYMBOLS-EMPTY:
## armv6.yaml
## CPUTYPE: CPU_TYPE_ARM
## CPUSUBTYPE: CPU_SUBTYPE_ARM_V6
--- !mach-o
FileHeader:
magic: 0xFEEDFACE
cputype: 0x0000000C
cpusubtype: 0x00000006
filetype: 0x00000001
ncmds: 2
sizeofcmds: 148
flags: 0x00002000
LoadCommands:
- cmd: LC_SEGMENT
cmdsize: 124
segname: ''
vmaddr: 0
vmsize: 24
fileoff: 296
filesize: 24
maxprot: 7
initprot: 7
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
size: 24
offset: 0x00000128
align: 2
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
content: 04D04DE208009FE500008DE504D08DE21EFF2FE100000000
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 328
nsyms: 1
stroff: 340
strsize: 8
LinkEditData:
NameList:
- n_strx: 1
n_type: 0x0F
n_sect: 1
n_desc: 0
n_value: 0
StringTable:
- ''
- _armv6
- ''
...
## armv7.yaml
## CPUTYPE: CPU_TYPE_ARM
## CPUSUBTYPE: CPU_SUBTYPE_ARM_V7
--- !mach-o
FileHeader:
magic: 0xFEEDFACE
cputype: 0x0000000C
cpusubtype: 0x00000009
filetype: 0x00000001
ncmds: 2
sizeofcmds: 148
flags: 0x00002000
LoadCommands:
- cmd: LC_SEGMENT
cmdsize: 124
segname: ''
vmaddr: 0
vmsize: 10
fileoff: 280
filesize: 10
maxprot: 7
initprot: 7
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
size: 10
offset: 0x00000118
align: 1
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
content: 81B00020009001B07047
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 292
nsyms: 1
stroff: 304
strsize: 8
LinkEditData:
NameList:
- n_strx: 1
n_type: 0x0F
n_sect: 1
n_desc: 8
n_value: 0
StringTable:
- ''
- _armv7
- ''
...
# RUN: yaml2obj %s --docnum=3 -o %t.x86_64
# RUN: yaml2obj %s --docnum=4 -o %t.x86_64_h
# RUN: llvm-libtool-darwin -static -o %t.lib %t.x86_64 %t.x86_64_h -arch_only x86_64
## Check that only x86_64 binary is present:
# RUN: llvm-ar t %t.lib | \
# RUN: FileCheck %s --check-prefix=X86-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp
# X86-NAMES: [[PREFIX]].x86_64
## Check that only x86_64 symbol is present:
# RUN: llvm-nm --print-armap %t.lib | \
# RUN: FileCheck %s --check-prefix=X86-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines
# X86-SYMBOLS: Archive map
# X86-SYMBOLS-NEXT: _x86_64 in [[PREFIX]].x86_64
# X86-SYMBOLS-EMPTY:
## x86_64.yaml
## CPUTYPE: CPU_TYPE_X86_64
## CPUSUBTYPE: CPU_SUBTYPE_X86_64_ALL
--- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x01000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 2
sizeofcmds: 176
flags: 0x00002000
reserved: 0x00000000
LoadCommands:
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: ''
vmaddr: 0
vmsize: 15
fileoff: 312
filesize: 15
maxprot: 7
initprot: 7
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
size: 15
offset: 0x00000138
align: 4
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
content: 554889E531C0C745FC000000005DC3
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 328
nsyms: 1
stroff: 344
strsize: 8
LinkEditData:
NameList:
- n_strx: 1
n_type: 0x0F
n_sect: 1
n_desc: 0
n_value: 0
StringTable:
- ''
- _x86_64
- ''
...
## x86_64h.yaml
## CPUTYPE: CPU_TYPE_X86_64
## CPUSUBTYPE: CPU_SUBTYPE_X86_64_H
--- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x01000007
cpusubtype: 0x00000008
filetype: 0x00000001
ncmds: 2
sizeofcmds: 176
flags: 0x00002000
reserved: 0x00000000
LoadCommands:
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: ''
vmaddr: 0
vmsize: 15
fileoff: 312
filesize: 15
maxprot: 7
initprot: 7
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
size: 15
offset: 0x00000138
align: 4
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
content: 554889E531C0C745FC000000005DC3
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 328
nsyms: 1
stroff: 344
strsize: 8
LinkEditData:
NameList:
- n_strx: 1
n_type: 0x0F
n_sect: 1
n_desc: 0
n_value: 0
StringTable:
- ''
- _x86_64_h
- ''
...

View File

@ -0,0 +1,240 @@
## This test checks that a universal file is flattened correctly.
# RUN: yaml2obj %s -o %t-universal.o
# RUN: yaml2obj %S/Inputs/input1.yaml -o %t-input1.o
# RUN: yaml2obj %S/Inputs/input2.yaml -o %t-input2.o
# RUN: llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only arm64
## Check that the binary is present:
# RUN: llvm-ar t %t.lib | \
# RUN: FileCheck %s --check-prefix=CHECK-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp
# CHECK-NAMES: [[PREFIX]]-universal.o
## Check that symbols are present:
# RUN: llvm-nm --print-armap %t.lib | \
# RUN: FileCheck %s --check-prefix=CHECK-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines
# CHECK-SYMBOLS: Archive map
# CHECK-SYMBOLS-NEXT: _arm64 in [[PREFIX]]-universal.o
# CHECK-SYMBOLS-EMPTY:
## Check that the output archive is in Darwin format:
# RUN: llvm-objdump --macho --archive-headers %t.lib | \
# RUN: FileCheck %s --check-prefix=FORMAT -DPREFIX=%basename_t.tmp -DARCHIVE=%t.lib
# FORMAT: Archive : [[ARCHIVE]]
# FORMAT-NEXT: __.SYMDEF
# FORMAT-NEXT: [[PREFIX]]-universal.o
# FORMAT-NOT: {{.}}
## Passing both a universal file and an object file:
# RUN: llvm-libtool-darwin -static -o %t.lib %t-universal.o %t-input1.o -arch_only x86_64
# RUN: llvm-ar t %t.lib | \
# RUN: FileCheck %s --check-prefix=BOTH-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp
# RUN: llvm-nm --print-armap %t.lib | \
# RUN: FileCheck %s --check-prefix=BOTH-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines
# BOTH-NAMES: [[PREFIX]]-universal.o
# BOTH-NAMES-NEXT: [[PREFIX]]-input1.o
# BOTH-SYMBOLS: Archive map
# BOTH-SYMBOLS-NEXT: _x86_64 in [[PREFIX]]-universal.o
# BOTH-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o
# BOTH-SYMBOLS-EMPTY:
## Passing both a universal file and an object file but filtering out the object file:
# RUN: llvm-libtool-darwin -static -o %t.lib %t-universal.o %t-input1.o -arch_only arm64
# RUN: llvm-ar t %t.lib | \
# RUN: FileCheck %s --check-prefix=CHECK-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp
# RUN: llvm-nm --print-armap %t.lib | \
# RUN: FileCheck %s --check-prefix=CHECK-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines
## Universal file containing an archive:
# RUN: rm -f %t.ar
# RUN: llvm-ar cr %t.ar %t-input1.o %t-input2.o
# RUN: llvm-lipo %t.ar -create -output %t-fat-with-archive.o
# RUN: llvm-libtool-darwin -static -o %t.lib %t-fat-with-archive.o -arch_only x86_64
# RUN: llvm-ar t %t.lib | \
# RUN: FileCheck %s --check-prefix=ARCHIVE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp
# RUN: llvm-nm --print-armap %t.lib | \
# RUN: FileCheck %s --check-prefix=ARCHIVE-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines
# ARCHIVE-NAMES: [[PREFIX]]-input1.o
# ARCHIVE-NAMES-NEXT: [[PREFIX]]-input2.o
# ARCHIVE-SYMBOLS: Archive map
# ARCHIVE-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o
# ARCHIVE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o
# ARCHIVE-SYMBOLS-EMPTY:
## Allow arch_only to be specified more than once (pick the last one):
# RUN: llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only arm64 -arch_only x86_64
# RUN: llvm-ar t %t.lib | \
# RUN: FileCheck %s --check-prefix=DOUBLE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp
# RUN: llvm-nm --print-armap %t.lib | \
# RUN: FileCheck %s --check-prefix=DOUBLE-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines
# DOUBLE-NAMES: [[PREFIX]]-universal.o
# DOUBLE-SYMBOLS: Archive map
# DOUBLE-SYMBOLS-NEXT: _x86_64 in [[PREFIX]]-universal.o
# DOUBLE-SYMBOLS-EMPTY:
## Invalid architecture:
# RUN: not llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only arch101 2>&1 | \
# RUN: FileCheck %s --check-prefix=INVALID-ARCH
# INVALID-ARCH: invalid architecture 'arch101': valid architecture names are
## Empty architecture:
# RUN: not llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only "" 2>&1 | \
# RUN: FileCheck %s --check-prefix=EMPTY-ARCH
# EMPTY-ARCH: invalid architecture '': valid architecture names are
## Missing architecture:
# RUN: not llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only ppc 2>&1 | \
# RUN: FileCheck %s --check-prefix=MISSING-ARCH
# MISSING-ARCH: error: no library created (no object files in input files matching -arch_only ppc)
## arch_only missing argument:
# RUN: not llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only 2>&1 | \
# RUN: FileCheck %s --check-prefix=REQUIRE-ARCH
# REQUIRE-ARCH: for the --arch_only option: requires a value!
## x86_64-arm64-universal.yaml
--- !fat-mach-o
FatHeader:
magic: 0xCAFEBABE
nfat_arch: 2
FatArchs:
- cputype: 0x01000007
cpusubtype: 0x00000003
offset: 0x0000000000001000
size: 352
align: 12
- cputype: 0x0100000C
cpusubtype: 0x00000000
offset: 0x0000000000004000
size: 384
align: 14
Slices:
- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x01000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 2
sizeofcmds: 176
flags: 0x00002000
reserved: 0x00000000
LoadCommands:
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: ''
vmaddr: 0
vmsize: 15
fileoff: 312
filesize: 15
maxprot: 7
initprot: 7
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
size: 15
offset: 0x00000138
align: 4
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
content: 554889E531C0C745FC000000005DC3
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 328
nsyms: 1
stroff: 344
strsize: 8
LinkEditData:
NameList:
- n_strx: 1
n_type: 0x0F
n_sect: 1
n_desc: 0
n_value: 0
StringTable:
- ''
- _x86_64
- ''
- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x0100000C
cpusubtype: 0x00000000
filetype: 0x00000001
ncmds: 2
sizeofcmds: 176
flags: 0x00002000
reserved: 0x00000000
LoadCommands:
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: ''
vmaddr: 0
vmsize: 24
fileoff: 312
filesize: 24
maxprot: 7
initprot: 7
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
size: 24
offset: 0x00000138
align: 2
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
content: FF4300D1FF0F00B908008052E00308AAFF430091C0035FD6
- cmd: LC_SYMTAB
cmdsize: 24
symoff: 336
nsyms: 2
stroff: 368
strsize: 16
LinkEditData:
NameList:
- n_strx: 7
n_type: 0x0E
n_sect: 1
n_desc: 0
n_value: 0
- n_strx: 1
n_type: 0x0F
n_sect: 1
n_desc: 0
n_value: 0
StringTable:
- ''
- _arm64
- ltmp0
- ''
- ''
- ''
...

View File

@ -2,6 +2,7 @@ set(LLVM_LINK_COMPONENTS
BinaryFormat BinaryFormat
Object Object
Support Support
TextAPI
) )
add_llvm_tool(llvm-libtool-darwin add_llvm_tool(llvm-libtool-darwin

View File

@ -17,4 +17,4 @@
type = Tool type = Tool
name = llvm-libtool-darwin name = llvm-libtool-darwin
parent = Tools parent = Tools
required_libraries = BinaryFormat Object Support required_libraries = BinaryFormat Object Support TextAPI

View File

@ -13,11 +13,13 @@
#include "llvm/BinaryFormat/Magic.h" #include "llvm/BinaryFormat/Magic.h"
#include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/MachO.h" #include "llvm/Object/MachO.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h" #include "llvm/Object/ObjectFile.h"
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h" #include "llvm/Support/InitLLVM.h"
#include "llvm/Support/LineIterator.h" #include "llvm/Support/LineIterator.h"
#include "llvm/Support/WithColor.h" #include "llvm/Support/WithColor.h"
#include "llvm/TextAPI/MachO/Architecture.h"
using namespace llvm; using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
@ -33,6 +35,10 @@ static cl::list<std::string> InputFiles(cl::Positional,
cl::ZeroOrMore, cl::ZeroOrMore,
cl::cat(LibtoolCategory)); cl::cat(LibtoolCategory));
static cl::opt<std::string> ArchType(
"arch_only", cl::desc("Specify architecture type for output library"),
cl::value_desc("arch_type"), cl::ZeroOrMore, cl::cat(LibtoolCategory));
enum class Operation { Static }; enum class Operation { Static };
static cl::opt<Operation> LibraryOperation( static cl::opt<Operation> LibraryOperation(
@ -92,7 +98,51 @@ static Error processFileList() {
return Error::success(); return Error::success();
} }
static Error verifyMachOObject(const NewArchiveMember &Member) { static Error validateArchitectureName(StringRef ArchitectureName) {
if (!MachOObjectFile::isValidArch(ArchitectureName)) {
std::string Buf;
raw_string_ostream OS(Buf);
for (StringRef Arch : MachOObjectFile::getValidArchs())
OS << Arch << " ";
return createStringError(
std::errc::invalid_argument,
"invalid architecture '%s': valid architecture names are %s",
ArchitectureName.str().c_str(), OS.str().c_str());
}
return Error::success();
}
// Check that a file's architecture [FileCPUType, FileCPUSubtype]
// matches the architecture specified under -arch_only flag.
static bool acceptFileArch(uint32_t FileCPUType, uint32_t FileCPUSubtype) {
uint32_t ArchCPUType, ArchCPUSubtype;
std::tie(ArchCPUType, ArchCPUSubtype) = MachO::getCPUTypeFromArchitecture(
MachO::getArchitectureFromName(ArchType));
if (ArchCPUType != FileCPUType)
return false;
switch (ArchCPUType) {
case MachO::CPU_TYPE_ARM:
case MachO::CPU_TYPE_ARM64_32:
case MachO::CPU_TYPE_X86_64:
return ArchCPUSubtype == FileCPUSubtype;
case MachO::CPU_TYPE_ARM64:
if (ArchCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL)
return FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL ||
FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_V8;
else
return ArchCPUSubtype == FileCPUSubtype;
default:
return true;
}
}
static Error verifyAndAddMachOObject(std::vector<NewArchiveMember> &Members,
NewArchiveMember Member) {
auto MBRef = Member.Buf->getMemBufferRef(); auto MBRef = Member.Buf->getMemBufferRef();
Expected<std::unique_ptr<object::ObjectFile>> ObjOrErr = Expected<std::unique_ptr<object::ObjectFile>> ObjOrErr =
object::ObjectFile::createObjectFile(MBRef); object::ObjectFile::createObjectFile(MBRef);
@ -107,6 +157,18 @@ static Error verifyMachOObject(const NewArchiveMember &Member) {
"'%s': format not supported", "'%s': format not supported",
Member.MemberName.data()); Member.MemberName.data());
auto *O = dyn_cast<MachOObjectFile>(ObjOrErr->get());
uint32_t FileCPUType, FileCPUSubtype;
std::tie(FileCPUType, FileCPUSubtype) = MachO::getCPUTypeFromArchitecture(
MachO::getArchitectureFromName(O->getArchTriple().getArchName()));
// If -arch_only is specified then skip this file if it doesn't match
// the architecture specified.
if (!ArchType.empty() && !acceptFileArch(FileCPUType, FileCPUSubtype)) {
return Error::success();
}
Members.push_back(std::move(Member));
return Error::success(); return Error::success();
} }
@ -117,18 +179,94 @@ static Error addChildMember(std::vector<NewArchiveMember> &Members,
if (!NMOrErr) if (!NMOrErr)
return NMOrErr.takeError(); return NMOrErr.takeError();
// Verify that Member is a Mach-O object file. if (Error E = verifyAndAddMachOObject(Members, std::move(*NMOrErr)))
if (Error E = verifyMachOObject(*NMOrErr))
return E; return E;
Members.push_back(std::move(*NMOrErr)); return Error::success();
}
static Error processArchive(std::vector<NewArchiveMember> &Members,
object::Archive &Lib, StringRef FileName,
const Config &C) {
Error Err = Error::success();
for (const object::Archive::Child &Child : Lib.children(Err))
if (Error E = addChildMember(Members, Child, C))
return createFileError(FileName, std::move(E));
if (Err)
return createFileError(FileName, std::move(Err));
return Error::success(); return Error::success();
} }
static Error static Error
addMember(std::vector<NewArchiveMember> &Members, StringRef FileName, addArchiveMembers(std::vector<NewArchiveMember> &Members,
std::vector<std::unique_ptr<MemoryBuffer>> &ArchiveBuffers, std::vector<std::unique_ptr<MemoryBuffer>> &ArchiveBuffers,
const Config &C) { NewArchiveMember NM, StringRef FileName, const Config &C) {
Expected<std::unique_ptr<Archive>> LibOrErr =
object::Archive::create(NM.Buf->getMemBufferRef());
if (!LibOrErr)
return createFileError(FileName, LibOrErr.takeError());
if (Error E = processArchive(Members, **LibOrErr, FileName, C))
return E;
// Update vector ArchiveBuffers with the MemoryBuffers to transfer
// ownership.
ArchiveBuffers.push_back(std::move(NM.Buf));
return Error::success();
}
static Error addUniversalMembers(
std::vector<NewArchiveMember> &Members,
std::vector<std::unique_ptr<MemoryBuffer>> &UniversalBuffers,
NewArchiveMember NM, StringRef FileName, const Config &C) {
Expected<std::unique_ptr<MachOUniversalBinary>> BinaryOrErr =
MachOUniversalBinary::create(NM.Buf->getMemBufferRef());
if (!BinaryOrErr)
return createFileError(FileName, BinaryOrErr.takeError());
auto *UO = BinaryOrErr->get();
for (const MachOUniversalBinary::ObjectForArch &O : UO->objects()) {
Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrErr =
O.getAsObjectFile();
if (MachOObjOrErr) {
NewArchiveMember NewMember =
NewArchiveMember(MachOObjOrErr->get()->getMemoryBufferRef());
NewMember.MemberName = sys::path::filename(NewMember.MemberName);
if (Error E = verifyAndAddMachOObject(Members, std::move(NewMember)))
return E;
continue;
}
Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive();
if (ArchiveOrError) {
// A universal file member can either be a MachOObjectFile or an Archive.
// In case we can successfully cast the member as an Archive, it is safe
// to throw away the error generated due to casting the object as a
// MachOObjectFile.
consumeError(MachOObjOrErr.takeError());
if (Error E = processArchive(Members, **ArchiveOrError, FileName, C))
return E;
continue;
}
Error CombinedError =
joinErrors(ArchiveOrError.takeError(), MachOObjOrErr.takeError());
return createFileError(FileName, std::move(CombinedError));
}
// Update vector UniversalBuffers with the MemoryBuffers to transfer
// ownership.
UniversalBuffers.push_back(std::move(NM.Buf));
return Error::success();
}
static Error addMember(std::vector<NewArchiveMember> &Members,
std::vector<std::unique_ptr<MemoryBuffer>> &FileBuffers,
StringRef FileName, const Config &C) {
Expected<NewArchiveMember> NMOrErr = Expected<NewArchiveMember> NMOrErr =
NewArchiveMember::getFile(FileName, C.Deterministic); NewArchiveMember::getFile(FileName, C.Deterministic);
if (!NMOrErr) if (!NMOrErr)
@ -137,43 +275,36 @@ addMember(std::vector<NewArchiveMember> &Members, StringRef FileName,
// For regular archives, use the basename of the object path for the member // For regular archives, use the basename of the object path for the member
// name. // name.
NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName);
file_magic Magic = identify_magic(NMOrErr->Buf->getBuffer());
// Flatten archives. // Flatten archives.
if (identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { if (Magic == file_magic::archive)
Expected<std::unique_ptr<Archive>> LibOrErr = return addArchiveMembers(Members, FileBuffers, std::move(*NMOrErr),
object::Archive::create(NMOrErr->Buf->getMemBufferRef()); FileName, C);
if (!LibOrErr)
return createFileError(FileName, LibOrErr.takeError());
object::Archive &Lib = **LibOrErr;
Error Err = Error::success(); // Flatten universal files.
for (const object::Archive::Child &Child : Lib.children(Err)) if (Magic == file_magic::macho_universal_binary)
if (Error E = addChildMember(Members, Child, C)) return addUniversalMembers(Members, FileBuffers, std::move(*NMOrErr),
return createFileError(FileName, std::move(E)); FileName, C);
if (Err)
return createFileError(FileName, std::move(Err));
// Update vector ArchiveBuffers with the MemoryBuffers to transfer if (Error E = verifyAndAddMachOObject(Members, std::move(*NMOrErr)))
// ownership.
ArchiveBuffers.push_back(std::move(NMOrErr->Buf));
return Error::success();
}
// Verify that Member is a Mach-O object file.
if (Error E = verifyMachOObject(*NMOrErr))
return E; return E;
Members.push_back(std::move(*NMOrErr));
return Error::success(); return Error::success();
} }
static Error createStaticLibrary(const Config &C) { static Error createStaticLibrary(const Config &C) {
std::vector<NewArchiveMember> NewMembers; std::vector<NewArchiveMember> NewMembers;
std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; std::vector<std::unique_ptr<MemoryBuffer>> FileBuffers;
for (StringRef Member : InputFiles) for (StringRef FileName : InputFiles)
if (Error E = addMember(NewMembers, Member, ArchiveBuffers, C)) if (Error E = addMember(NewMembers, FileBuffers, FileName, C))
return E; return E;
if (NewMembers.empty() && !ArchType.empty())
return createStringError(std::errc::invalid_argument,
"no library created (no object files in input "
"files matching -arch_only %s)",
ArchType.c_str());
if (Error E = if (Error E =
writeArchive(OutputFile, NewMembers, writeArchive(OutputFile, NewMembers,
/*WriteSymtab=*/true, /*WriteSymtab=*/true,
@ -201,6 +332,10 @@ static Expected<Config> parseCommandLine(int Argc, char **Argv) {
return createStringError(std::errc::invalid_argument, return createStringError(std::errc::invalid_argument,
"no input files specified"); "no input files specified");
if (ArchType.getNumOccurrences())
if (Error E = validateArchitectureName(ArchType))
return std::move(E);
return C; return C;
} }