Skip to content

Commit 282bf21

Browse files
[llvm-nm][MachO] Add support for MH_FILESET
Support printing of symbols for MachO of `MH_FILESET` type. This is achieved by extending `dumpSymbolNamesFromObject` to encompass fileset handling, and including an offset in `MachOObjectFile` class to locate embedded MachO headers. Differential Revision: https://reviews.llvm.org/D159294
1 parent d227c8a commit 282bf21

File tree

8 files changed

+136
-43
lines changed

8 files changed

+136
-43
lines changed

llvm/include/llvm/BinaryFormat/MachO.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -898,12 +898,17 @@ struct linker_option_command {
898898
uint32_t count;
899899
};
900900

901+
union lc_str {
902+
uint32_t offset;
903+
};
904+
901905
struct fileset_entry_command {
902906
uint32_t cmd;
903907
uint32_t cmdsize;
904908
uint64_t vmaddr;
905909
uint64_t fileoff;
906-
uint32_t entry_id;
910+
union lc_str entry_id;
911+
uint32_t reserved;
907912
};
908913

909914
// The symseg_command is obsolete and no longer supported.
@@ -1434,7 +1439,8 @@ inline void swapStruct(fileset_entry_command &C) {
14341439
sys::swapByteOrder(C.cmdsize);
14351440
sys::swapByteOrder(C.vmaddr);
14361441
sys::swapByteOrder(C.fileoff);
1437-
sys::swapByteOrder(C.entry_id);
1442+
sys::swapByteOrder(C.entry_id.offset);
1443+
sys::swapByteOrder(C.reserved);
14381444
}
14391445

14401446
inline void swapStruct(version_min_command &C) {

llvm/include/llvm/Object/MachO.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,8 @@ class MachOObjectFile : public ObjectFile {
414414

415415
static Expected<std::unique_ptr<MachOObjectFile>>
416416
create(MemoryBufferRef Object, bool IsLittleEndian, bool Is64Bits,
417-
uint32_t UniversalCputype = 0, uint32_t UniversalIndex = 0);
417+
uint32_t UniversalCputype = 0, uint32_t UniversalIndex = 0,
418+
size_t MachOFilesetEntryOffset = 0);
418419

419420
static bool isMachOPairedReloc(uint64_t RelocType, uint64_t Arch);
420421

@@ -697,6 +698,8 @@ class MachOObjectFile : public ObjectFile {
697698
getRoutinesCommand64(const LoadCommandInfo &L) const;
698699
MachO::thread_command
699700
getThreadCommand(const LoadCommandInfo &L) const;
701+
MachO::fileset_entry_command
702+
getFilesetEntryLoadCommand(const LoadCommandInfo &L) const;
700703

701704
MachO::any_relocation_info getRelocation(DataRefImpl Rel) const;
702705
MachO::data_in_code_entry getDice(DataRefImpl Rel) const;
@@ -760,6 +763,8 @@ class MachOObjectFile : public ObjectFile {
760763

761764
bool hasPageZeroSegment() const { return HasPageZeroSegment; }
762765

766+
size_t getMachOFilesetEntryOffset() const { return MachOFilesetEntryOffset; }
767+
763768
static bool classof(const Binary *v) {
764769
return v->isMachO();
765770
}
@@ -839,7 +844,8 @@ class MachOObjectFile : public ObjectFile {
839844
private:
840845
MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, bool Is64Bits,
841846
Error &Err, uint32_t UniversalCputype = 0,
842-
uint32_t UniversalIndex = 0);
847+
uint32_t UniversalIndex = 0,
848+
size_t MachOFilesetEntryOffset = 0);
843849

844850
uint64_t getSymbolValueImpl(DataRefImpl Symb) const override;
845851

@@ -867,6 +873,7 @@ class MachOObjectFile : public ObjectFile {
867873
const char *DyldExportsTrieLoadCmd = nullptr;
868874
const char *UuidLoadCmd = nullptr;
869875
bool HasPageZeroSegment = false;
876+
size_t MachOFilesetEntryOffset = 0;
870877
};
871878

872879
/// DiceRef

llvm/include/llvm/Object/ObjectFile.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,9 +389,9 @@ class ObjectFile : public SymbolicFile {
389389
createELFObjectFile(MemoryBufferRef Object, bool InitContent = true);
390390

391391
static Expected<std::unique_ptr<MachOObjectFile>>
392-
createMachOObjectFile(MemoryBufferRef Object,
393-
uint32_t UniversalCputype = 0,
394-
uint32_t UniversalIndex = 0);
392+
createMachOObjectFile(MemoryBufferRef Object, uint32_t UniversalCputype = 0,
393+
uint32_t UniversalIndex = 0,
394+
size_t MachOFilesetEntryOffset = 0);
395395

396396
static Expected<std::unique_ptr<ObjectFile>>
397397
createGOFFObjectFile(MemoryBufferRef Object);

llvm/lib/Object/MachOObjectFile.cpp

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,11 @@ getSectionPtr(const MachOObjectFile &O, MachOObjectFile::LoadCommandInfo L,
108108
return reinterpret_cast<const char*>(SectionAddr);
109109
}
110110

111-
static const char *getPtr(const MachOObjectFile &O, size_t Offset) {
112-
assert(Offset <= O.getData().size());
113-
return O.getData().data() + Offset;
111+
static const char *getPtr(const MachOObjectFile &O, size_t Offset,
112+
size_t MachOFilesetEntryOffset = 0) {
113+
assert(Offset <= O.getData().size() &&
114+
MachOFilesetEntryOffset <= O.getData().size());
115+
return O.getData().data() + Offset + MachOFilesetEntryOffset;
114116
}
115117

116118
static MachO::nlist_base
@@ -208,7 +210,8 @@ getFirstLoadCommandInfo(const MachOObjectFile &Obj) {
208210
if (sizeof(MachO::load_command) > Obj.getHeader().sizeofcmds)
209211
return malformedError("load command 0 extends past the end all load "
210212
"commands in the file");
211-
return getLoadCommandInfo(Obj, getPtr(Obj, HeaderSize), 0);
213+
return getLoadCommandInfo(
214+
Obj, getPtr(Obj, HeaderSize, Obj.getMachOFilesetEntryOffset()), 0);
212215
}
213216

214217
static Expected<MachOObjectFile::LoadCommandInfo>
@@ -217,7 +220,8 @@ getNextLoadCommandInfo(const MachOObjectFile &Obj, uint32_t LoadCommandIndex,
217220
unsigned HeaderSize = Obj.is64Bit() ? sizeof(MachO::mach_header_64)
218221
: sizeof(MachO::mach_header);
219222
if (L.Ptr + L.C.cmdsize + sizeof(MachO::load_command) >
220-
Obj.getData().data() + HeaderSize + Obj.getHeader().sizeofcmds)
223+
Obj.getData().data() + Obj.getMachOFilesetEntryOffset() + HeaderSize +
224+
Obj.getHeader().sizeofcmds)
221225
return malformedError("load command " + Twine(LoadCommandIndex + 1) +
222226
" extends past the end all load commands in the file");
223227
return getLoadCommandInfo(Obj, L.Ptr + L.C.cmdsize, LoadCommandIndex + 1);
@@ -231,7 +235,8 @@ static void parseHeader(const MachOObjectFile &Obj, T &Header,
231235
"file");
232236
return;
233237
}
234-
if (auto HeaderOrErr = getStructOrErr<T>(Obj, getPtr(Obj, 0)))
238+
if (auto HeaderOrErr = getStructOrErr<T>(
239+
Obj, getPtr(Obj, 0, Obj.getMachOFilesetEntryOffset())))
235240
Header = *HeaderOrErr;
236241
else
237242
Err = HeaderOrErr.takeError();
@@ -1247,12 +1252,12 @@ static bool isLoadCommandObsolete(uint32_t cmd) {
12471252
Expected<std::unique_ptr<MachOObjectFile>>
12481253
MachOObjectFile::create(MemoryBufferRef Object, bool IsLittleEndian,
12491254
bool Is64Bits, uint32_t UniversalCputype,
1250-
uint32_t UniversalIndex) {
1255+
uint32_t UniversalIndex,
1256+
size_t MachOFilesetEntryOffset) {
12511257
Error Err = Error::success();
1252-
std::unique_ptr<MachOObjectFile> Obj(
1253-
new MachOObjectFile(std::move(Object), IsLittleEndian,
1254-
Is64Bits, Err, UniversalCputype,
1255-
UniversalIndex));
1258+
std::unique_ptr<MachOObjectFile> Obj(new MachOObjectFile(
1259+
std::move(Object), IsLittleEndian, Is64Bits, Err, UniversalCputype,
1260+
UniversalIndex, MachOFilesetEntryOffset));
12561261
if (Err)
12571262
return std::move(Err);
12581263
return std::move(Obj);
@@ -1261,8 +1266,10 @@ MachOObjectFile::create(MemoryBufferRef Object, bool IsLittleEndian,
12611266
MachOObjectFile::MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian,
12621267
bool Is64bits, Error &Err,
12631268
uint32_t UniversalCputype,
1264-
uint32_t UniversalIndex)
1265-
: ObjectFile(getMachOType(IsLittleEndian, Is64bits), Object) {
1269+
uint32_t UniversalIndex,
1270+
size_t MachOFilesetEntryOffset)
1271+
: ObjectFile(getMachOType(IsLittleEndian, Is64bits), Object),
1272+
MachOFilesetEntryOffset(MachOFilesetEntryOffset) {
12661273
ErrorAsOutParameter ErrAsOutParam(&Err);
12671274
uint64_t SizeOfHeaders;
12681275
uint32_t cputype;
@@ -4761,6 +4768,11 @@ MachOObjectFile::getThreadCommand(const LoadCommandInfo &L) const {
47614768
return getStruct<MachO::thread_command>(*this, L.Ptr);
47624769
}
47634770

4771+
MachO::fileset_entry_command
4772+
MachOObjectFile::getFilesetEntryLoadCommand(const LoadCommandInfo &L) const {
4773+
return getStruct<MachO::fileset_entry_command>(*this, L.Ptr);
4774+
}
4775+
47644776
MachO::any_relocation_info
47654777
MachOObjectFile::getRelocation(DataRefImpl Rel) const {
47664778
uint32_t Offset;
@@ -5300,23 +5312,29 @@ bool MachOObjectFile::isRelocatableObject() const {
53005312
return getHeader().filetype == MachO::MH_OBJECT;
53015313
}
53025314

5303-
Expected<std::unique_ptr<MachOObjectFile>>
5304-
ObjectFile::createMachOObjectFile(MemoryBufferRef Buffer,
5305-
uint32_t UniversalCputype,
5306-
uint32_t UniversalIndex) {
5315+
/// Create a MachOObjectFile instance from a given buffer.
5316+
///
5317+
/// \param Buffer Memory buffer containing the MachO binary data.
5318+
/// \param UniversalCputype CPU type when the MachO part of a universal binary.
5319+
/// \param UniversalIndex Index of the MachO within a universal binary.
5320+
/// \param MachOFilesetEntryOffset Offset of the MachO entry in a fileset MachO.
5321+
/// \returns A std::unique_ptr to a MachOObjectFile instance on success.
5322+
Expected<std::unique_ptr<MachOObjectFile>> ObjectFile::createMachOObjectFile(
5323+
MemoryBufferRef Buffer, uint32_t UniversalCputype, uint32_t UniversalIndex,
5324+
size_t MachOFilesetEntryOffset) {
53075325
StringRef Magic = Buffer.getBuffer().slice(0, 4);
53085326
if (Magic == "\xFE\xED\xFA\xCE")
5309-
return MachOObjectFile::create(Buffer, false, false,
5310-
UniversalCputype, UniversalIndex);
5327+
return MachOObjectFile::create(Buffer, false, false, UniversalCputype,
5328+
UniversalIndex, MachOFilesetEntryOffset);
53115329
if (Magic == "\xCE\xFA\xED\xFE")
5312-
return MachOObjectFile::create(Buffer, true, false,
5313-
UniversalCputype, UniversalIndex);
5330+
return MachOObjectFile::create(Buffer, true, false, UniversalCputype,
5331+
UniversalIndex, MachOFilesetEntryOffset);
53145332
if (Magic == "\xFE\xED\xFA\xCF")
5315-
return MachOObjectFile::create(Buffer, false, true,
5316-
UniversalCputype, UniversalIndex);
5333+
return MachOObjectFile::create(Buffer, false, true, UniversalCputype,
5334+
UniversalIndex, MachOFilesetEntryOffset);
53175335
if (Magic == "\xCF\xFA\xED\xFE")
5318-
return MachOObjectFile::create(Buffer, true, true,
5319-
UniversalCputype, UniversalIndex);
5336+
return MachOObjectFile::create(Buffer, true, true, UniversalCputype,
5337+
UniversalIndex, MachOFilesetEntryOffset);
53205338
return make_error<GenericBinaryError>("Unrecognized MachO magic number",
53215339
object_error::invalid_file_type);
53225340
}

llvm/lib/ObjectYAML/MachOYAML.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,8 @@ void MappingTraits<MachO::fileset_entry_command>::mapping(
627627
IO &IO, MachO::fileset_entry_command &LoadCommand) {
628628
IO.mapRequired("vmaddr", LoadCommand.vmaddr);
629629
IO.mapRequired("fileoff", LoadCommand.fileoff);
630-
IO.mapRequired("id", LoadCommand.entry_id);
630+
IO.mapRequired("id", LoadCommand.entry_id.offset);
631+
IO.mapOptional("reserved", LoadCommand.reserved);
631632
}
632633

633634
} // end namespace yaml
Binary file not shown.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
RUN: llvm-nm %p/Inputs/fileset.macho-aarch64 | FileCheck %s
2+
3+
CHECK: Symbols for fileset_entry:
4+
CHECK-NEXT: 0000000000004010 s bar
5+
CHECK-NEXT: 0000000000004000 s foo

llvm/tools/llvm-nm/llvm-nm.cpp

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "llvm/ADT/StringSwitch.h"
1919
#include "llvm/BinaryFormat/COFF.h"
20+
#include "llvm/BinaryFormat/MachO.h"
2021
#include "llvm/BinaryFormat/XCOFF.h"
2122
#include "llvm/Demangle/Demangle.h"
2223
#include "llvm/IR/Function.h"
@@ -29,6 +30,7 @@
2930
#include "llvm/Object/MachO.h"
3031
#include "llvm/Object/MachOUniversal.h"
3132
#include "llvm/Object/ObjectFile.h"
33+
#include "llvm/Object/SymbolicFile.h"
3234
#include "llvm/Object/TapiFile.h"
3335
#include "llvm/Object/TapiUniversal.h"
3436
#include "llvm/Object/Wasm.h"
@@ -1890,26 +1892,18 @@ static Expected<bool> hasSymbols(SymbolicFile &Obj) {
18901892
return !Obj.symbols().empty();
18911893
}
18921894

1893-
static void dumpSymbolNamesFromObject(
1895+
static void printSymbolNamesFromObject(
18941896
SymbolicFile &Obj, std::vector<NMSymbol> &SymbolList,
18951897
bool PrintSymbolObject, bool PrintObjectLabel, StringRef ArchiveName = {},
18961898
StringRef ArchitectureName = {}, StringRef ObjectName = {},
18971899
bool PrintArchiveName = true) {
1898-
if (!shouldDump(Obj))
1899-
return;
1900-
1901-
if (ExportSymbols && Obj.isXCOFF()) {
1902-
XCOFFObjectFile *XCOFFObj = cast<XCOFFObjectFile>(&Obj);
1903-
getXCOFFExports(XCOFFObj, SymbolList, ArchiveName);
1904-
return;
1905-
}
19061900

19071901
if (PrintObjectLabel && !ExportSymbols)
19081902
printObjectLabel(PrintArchiveName, ArchiveName, ArchitectureName,
19091903
ObjectName.empty() ? Obj.getFileName() : ObjectName);
1904+
19101905
if (!getSymbolNamesFromObject(Obj, SymbolList) || ExportSymbols)
19111906
return;
1912-
CurrentFilename = Obj.getFileName();
19131907

19141908
// If there is an error in hasSymbols(), the error should be encountered in
19151909
// function getSymbolNamesFromObject first.
@@ -1923,6 +1917,68 @@ static void dumpSymbolNamesFromObject(
19231917
ArchitectureName);
19241918
}
19251919

1920+
static void dumpSymbolsNameFromMachOFilesetEntry(
1921+
MachOObjectFile *Obj, std::vector<NMSymbol> &SymbolList,
1922+
bool PrintSymbolObject, bool PrintObjectLabel) {
1923+
auto Buf = Obj->getMemoryBufferRef();
1924+
const auto *End = Obj->load_commands().end();
1925+
for (const auto *It = Obj->load_commands().begin(); It != End; ++It) {
1926+
const auto &Command = *It;
1927+
if (Command.C.cmd != MachO::LC_FILESET_ENTRY)
1928+
continue;
1929+
1930+
MachO::fileset_entry_command Entry =
1931+
Obj->getFilesetEntryLoadCommand(Command);
1932+
auto MaybeMachO =
1933+
MachOObjectFile::createMachOObjectFile(Buf, 0, 0, Entry.fileoff);
1934+
1935+
if (Error Err = MaybeMachO.takeError())
1936+
report_fatal_error(std::move(Err));
1937+
1938+
const char *EntryName = Command.Ptr + Entry.entry_id.offset;
1939+
if (EntryName)
1940+
outs() << "Symbols for " << EntryName << ": \n";
1941+
1942+
std::unique_ptr<MachOObjectFile> EntryMachO = std::move(MaybeMachO.get());
1943+
printSymbolNamesFromObject(*EntryMachO, SymbolList, PrintSymbolObject,
1944+
PrintObjectLabel);
1945+
1946+
if (std::next(It) != End)
1947+
outs() << "\n";
1948+
}
1949+
}
1950+
1951+
static void dumpSymbolNamesFromObject(
1952+
SymbolicFile &Obj, std::vector<NMSymbol> &SymbolList,
1953+
bool PrintSymbolObject, bool PrintObjectLabel, StringRef ArchiveName = {},
1954+
StringRef ArchitectureName = {}, StringRef ObjectName = {},
1955+
bool PrintArchiveName = true) {
1956+
if (!shouldDump(Obj))
1957+
return;
1958+
1959+
if (ExportSymbols && Obj.isXCOFF()) {
1960+
XCOFFObjectFile *XCOFFObj = cast<XCOFFObjectFile>(&Obj);
1961+
getXCOFFExports(XCOFFObj, SymbolList, ArchiveName);
1962+
return;
1963+
}
1964+
1965+
CurrentFilename = Obj.getFileName();
1966+
1967+
// Are we handling a MachO of type MH_FILESET?
1968+
if (Obj.isMachO() && Obj.is64Bit() &&
1969+
cast<MachOObjectFile>(&Obj)->getHeader64().filetype ==
1970+
MachO::MH_FILESET) {
1971+
dumpSymbolsNameFromMachOFilesetEntry(cast<MachOObjectFile>(&Obj),
1972+
SymbolList, PrintSymbolObject,
1973+
PrintObjectLabel);
1974+
return;
1975+
}
1976+
1977+
printSymbolNamesFromObject(Obj, SymbolList, PrintSymbolObject,
1978+
PrintObjectLabel, ArchiveName, ArchitectureName,
1979+
ObjectName, PrintArchiveName);
1980+
}
1981+
19261982
// checkMachOAndArchFlags() checks to see if the SymbolicFile is a Mach-O file
19271983
// and if it is and there is a list of architecture flags is specified then
19281984
// check to make sure this Mach-O file is one of those architectures or all

0 commit comments

Comments
 (0)