[llvm] 47e4663 - [llvm-objdump] Add -dyld_info to llvm-otool
Daniel Bertalan via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 28 00:35:41 PDT 2022
Author: Daniel Bertalan
Date: 2022-08-28T09:22:41+02:00
New Revision: 47e4663c4eacaedab63e407651f5b045446bb36d
URL: https://github.com/llvm/llvm-project/commit/47e4663c4eacaedab63e407651f5b045446bb36d
DIFF: https://github.com/llvm/llvm-project/commit/47e4663c4eacaedab63e407651f5b045446bb36d.diff
LOG: [llvm-objdump] Add -dyld_info to llvm-otool
This option outputs the location, encoded value and target of chained
fixups, using the same format as `otool -dyld_info`.
This initial implementation only supports the DYLD_CHAINED_PTR_64 and
DYLD_CHAINED_PTR_64_OFFSET pointer encodings, which are used in x86_64
and arm64 userspace binaries.
When Apple's effort to upstream their chained fixups code continues,
we'll replace this code with the then-upstreamed code. But we need
something in the meantime for testing ld64.lld's chained fixups code.
Differential Revision: https://reviews.llvm.org/D132036
Added:
Modified:
llvm/include/llvm/BinaryFormat/MachO.h
llvm/include/llvm/Object/MachO.h
llvm/lib/Object/MachOObjectFile.cpp
llvm/test/tools/llvm-objdump/MachO/dyld-info.test
llvm/tools/llvm-objdump/MachODump.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/BinaryFormat/MachO.h b/llvm/include/llvm/BinaryFormat/MachO.h
index a9fc7954d52ce..81b315a59ea9e 100644
--- a/llvm/include/llvm/BinaryFormat/MachO.h
+++ b/llvm/include/llvm/BinaryFormat/MachO.h
@@ -1103,6 +1103,27 @@ struct dyld_chained_import_addend64 {
uint64_t addend;
};
+// The `bind` field (most significant bit) of the encoded fixup determines
+// whether it is dyld_chained_ptr_64_bind or dyld_chained_ptr_64_rebase.
+
+// DYLD_CHAINED_PTR_64/DYLD_CHAINED_PTR_64_OFFSET
+struct dyld_chained_ptr_64_bind {
+ uint64_t ordinal : 24;
+ uint64_t addend : 8;
+ uint64_t reserved : 19;
+ uint64_t next : 12;
+ uint64_t bind : 1; // set to 1
+};
+
+// DYLD_CHAINED_PTR_64/DYLD_CHAINED_PTR_64_OFFSET
+struct dyld_chained_ptr_64_rebase {
+ uint64_t target : 36;
+ uint64_t high8 : 8;
+ uint64_t reserved : 7;
+ uint64_t next : 12;
+ uint64_t bind : 1; // set to 0
+};
+
// Byte order swapping functions for MachO structs
inline void swapStruct(fat_header &mh) {
diff --git a/llvm/include/llvm/Object/MachO.h b/llvm/include/llvm/Object/MachO.h
index ee928c1175a99..caeee6a13b341 100644
--- a/llvm/include/llvm/Object/MachO.h
+++ b/llvm/include/llvm/Object/MachO.h
@@ -296,6 +296,19 @@ struct ChainedFixupTarget {
bool WeakImport;
};
+struct ChainedFixupsSegment {
+ ChainedFixupsSegment(uint8_t SegIdx, uint32_t Offset,
+ const MachO::dyld_chained_starts_in_segment &Header,
+ std::vector<uint16_t> &&PageStarts)
+ : SegIdx(SegIdx), Offset(Offset), Header(Header),
+ PageStarts(PageStarts){};
+
+ uint32_t SegIdx;
+ uint32_t Offset; // dyld_chained_starts_in_image::seg_info_offset[SegIdx]
+ MachO::dyld_chained_starts_in_segment Header;
+ std::vector<uint16_t> PageStarts; // page_start[] entries, host endianness
+};
+
/// MachOAbstractFixupEntry is an abstract class representing a fixup in a
/// MH_DYLDLINK file. Fixups generally represent rebases and binds. Binds also
/// subdivide into additional subtypes (weak, lazy, reexport).
@@ -364,19 +377,29 @@ class MachOAbstractFixupEntry {
class MachOChainedFixupEntry : public MachOAbstractFixupEntry {
public:
- enum class FixupKind { All, Bind, WeakBind, Rebase };
+ enum class FixupKind { Bind, Rebase };
MachOChainedFixupEntry(Error *Err, const MachOObjectFile *O, bool Parse);
bool operator==(const MachOChainedFixupEntry &) const;
+ bool isBind() const { return Kind == FixupKind::Bind; }
+ bool isRebase() const { return Kind == FixupKind::Rebase; }
+
void moveNext();
void moveToFirst();
void moveToEnd();
private:
+ void findNextPageWithFixups();
+
std::vector<ChainedFixupTarget> FixupTargets;
- uint32_t FixupIndex = 0;
+ std::vector<ChainedFixupsSegment> Segments;
+ ArrayRef<uint8_t> SegmentData;
+ FixupKind Kind;
+ uint32_t InfoSegIndex = 0; // Index into Segments
+ uint32_t PageIndex = 0; // Index into Segments[InfoSegIdx].PageStarts
+ uint32_t PageOffset = 0; // Page offset of the current fixup
};
using fixup_iterator = content_iterator<MachOChainedFixupEntry>;
@@ -436,6 +459,7 @@ class MachOObjectFile : public ObjectFile {
/// Return the raw contents of an entire segment.
ArrayRef<uint8_t> getSegmentContents(StringRef SegmentName) const;
+ ArrayRef<uint8_t> getSegmentContents(size_t SegmentIndex) const;
/// When dsymutil generates the companion file, it strips all unnecessary
/// sections (e.g. everything in the _TEXT segment) by omitting their body
@@ -697,18 +721,6 @@ class MachOObjectFile : public ObjectFile {
// upstreams their implementation. Please do not rely on this.
Expected<Optional<MachO::linkedit_data_command>>
getChainedFixupsLoadCommand() const;
- struct ChainedFixupsSegment {
- ChainedFixupsSegment(uint8_t SegIdx, uint32_t Offset,
- const MachO::dyld_chained_starts_in_segment &Header,
- std::vector<uint16_t> &&PageStarts)
- : SegIdx(SegIdx), Offset(Offset), Header(Header),
- PageStarts(PageStarts){};
-
- uint32_t SegIdx;
- uint32_t Offset; // dyld_chained_starts_in_image::seg_info_offset[SegIdx]
- MachO::dyld_chained_starts_in_segment Header;
- std::vector<uint16_t> PageStarts; // page_start[] entries, host endianness
- };
// Returns the number of sections listed in dyld_chained_starts_in_image, and
// a ChainedFixupsSegment for each segment that has fixups.
Expected<std::pair<size_t, std::vector<ChainedFixupsSegment>>>
diff --git a/llvm/lib/Object/MachOObjectFile.cpp b/llvm/lib/Object/MachOObjectFile.cpp
index 0c202b2f189a3..0b06ec3b4d4fe 100644
--- a/llvm/lib/Object/MachOObjectFile.cpp
+++ b/llvm/lib/Object/MachOObjectFile.cpp
@@ -2073,6 +2073,19 @@ ArrayRef<uint8_t> getSegmentContents(const MachOObjectFile &Obj,
Segment.fileoff, Segment.fileoff + Segment.filesize));
return {};
}
+
+template <typename LoadCommandType>
+ArrayRef<uint8_t> getSegmentContents(const MachOObjectFile &Obj,
+ MachOObjectFile::LoadCommandInfo LoadCmd) {
+ auto SegmentOrErr = getStructOrErr<LoadCommandType>(Obj, LoadCmd.Ptr);
+ if (!SegmentOrErr) {
+ consumeError(SegmentOrErr.takeError());
+ return {};
+ }
+ auto &Segment = SegmentOrErr.get();
+ return arrayRefFromStringRef(
+ Obj.getData().slice(Segment.fileoff, Segment.fileoff + Segment.filesize));
+}
} // namespace
ArrayRef<uint8_t>
@@ -2097,6 +2110,28 @@ MachOObjectFile::getSegmentContents(StringRef SegmentName) const {
return {};
}
+ArrayRef<uint8_t>
+MachOObjectFile::getSegmentContents(size_t SegmentIndex) const {
+ size_t Idx = 0;
+ for (auto LoadCmd : load_commands()) {
+ switch (LoadCmd.C.cmd) {
+ case MachO::LC_SEGMENT:
+ if (Idx == SegmentIndex)
+ return ::getSegmentContents<MachO::segment_command>(*this, LoadCmd);
+ ++Idx;
+ break;
+ case MachO::LC_SEGMENT_64:
+ if (Idx == SegmentIndex)
+ return ::getSegmentContents<MachO::segment_command_64>(*this, LoadCmd);
+ ++Idx;
+ break;
+ default:
+ continue;
+ }
+ }
+ return {};
+}
+
unsigned MachOObjectFile::getSectionID(SectionRef Sec) const {
return Sec.getRawDataRefImpl().d.a;
}
@@ -3257,6 +3292,8 @@ void MachOAbstractFixupEntry::moveToFirst() {
void MachOAbstractFixupEntry::moveToEnd() { Done = true; }
+void MachOAbstractFixupEntry::moveNext() {}
+
MachOChainedFixupEntry::MachOChainedFixupEntry(Error *E,
const MachOObjectFile *O,
bool Parse)
@@ -3264,17 +3301,54 @@ MachOChainedFixupEntry::MachOChainedFixupEntry(Error *E,
ErrorAsOutParameter e(E);
if (!Parse)
return;
- if (auto FixupTargetsOrErr = O->getDyldChainedFixupTargets())
+
+ if (auto FixupTargetsOrErr = O->getDyldChainedFixupTargets()) {
FixupTargets = *FixupTargetsOrErr;
- else {
+ } else {
*E = FixupTargetsOrErr.takeError();
return;
}
+
+ if (auto SegmentsOrErr = O->getChainedFixupsSegments()) {
+ Segments = std::move(SegmentsOrErr->second);
+ } else {
+ *E = SegmentsOrErr.takeError();
+ return;
+ }
+}
+
+void MachOChainedFixupEntry::findNextPageWithFixups() {
+ auto FindInSegment = [this]() {
+ const ChainedFixupsSegment &SegInfo = Segments[InfoSegIndex];
+ while (PageIndex < SegInfo.PageStarts.size() &&
+ SegInfo.PageStarts[PageIndex] == MachO::DYLD_CHAINED_PTR_START_NONE)
+ ++PageIndex;
+ return PageIndex < SegInfo.PageStarts.size();
+ };
+
+ while (InfoSegIndex < Segments.size()) {
+ if (FindInSegment()) {
+ PageOffset = Segments[InfoSegIndex].PageStarts[PageIndex];
+ SegmentData = O->getSegmentContents(Segments[InfoSegIndex].SegIdx);
+ return;
+ }
+
+ InfoSegIndex++;
+ PageIndex = 0;
+ }
}
void MachOChainedFixupEntry::moveToFirst() {
MachOAbstractFixupEntry::moveToFirst();
- FixupIndex = 0;
+ if (Segments.empty()) {
+ Done = true;
+ return;
+ }
+
+ InfoSegIndex = 0;
+ PageIndex = 0;
+
+ findNextPageWithFixups();
moveNext();
}
@@ -3282,15 +3356,104 @@ void MachOChainedFixupEntry::moveToEnd() {
MachOAbstractFixupEntry::moveToEnd();
}
-void MachOChainedFixupEntry::moveNext() { Done = true; }
+void MachOChainedFixupEntry::moveNext() {
+ ErrorAsOutParameter ErrAsOutParam(E);
+
+ if (InfoSegIndex == Segments.size()) {
+ Done = true;
+ return;
+ }
+
+ const ChainedFixupsSegment &SegInfo = Segments[InfoSegIndex];
+ SegmentIndex = SegInfo.SegIdx;
+ SegmentOffset = SegInfo.Header.page_size * PageIndex + PageOffset;
+
+ // FIXME: Handle other pointer formats.
+ uint16_t PointerFormat = SegInfo.Header.pointer_format;
+ if (PointerFormat != MachO::DYLD_CHAINED_PTR_64 &&
+ PointerFormat != MachO::DYLD_CHAINED_PTR_64_OFFSET) {
+ *E = createError("segment " + Twine(SegmentIndex) +
+ " has unsupported chained fixup pointer_format " +
+ Twine(PointerFormat));
+ moveToEnd();
+ return;
+ }
+
+ Ordinal = 0;
+ Flags = 0;
+ Addend = 0;
+ PointerValue = 0;
+ SymbolName = {};
+
+ if (SegmentOffset + sizeof(RawValue) > SegmentData.size()) {
+ *E = malformedError("fixup in segment " + Twine(SegmentIndex) +
+ " at offset " + Twine(SegmentOffset) +
+ " extends past segment's end");
+ moveToEnd();
+ return;
+ }
+
+ static_assert(sizeof(RawValue) == sizeof(MachO::dyld_chained_import_addend));
+ memcpy(&RawValue, SegmentData.data() + SegmentOffset, sizeof(RawValue));
+ if (O->isLittleEndian() != sys::IsLittleEndianHost)
+ sys::swapByteOrder(RawValue);
+
+ // The bit extraction below assumes little-endian fixup entries.
+ assert(O->isLittleEndian() && "big-endian object should have been rejected "
+ "by getDyldChainedFixupTargets()");
+ auto Field = [this](uint8_t Right, uint8_t Count) {
+ return (RawValue >> Right) & ((1ULL << Count) - 1);
+ };
+
+ // The `bind` field (most significant bit) of the encoded fixup determines
+ // whether it is dyld_chained_ptr_64_bind or dyld_chained_ptr_64_rebase.
+ bool IsBind = Field(63, 1);
+ Kind = IsBind ? FixupKind::Bind : FixupKind::Rebase;
+ uint32_t Next = Field(51, 12);
+ if (IsBind) {
+ uint32_t ImportOrdinal = Field(0, 24);
+ uint8_t InlineAddend = Field(24, 8);
+
+ if (ImportOrdinal >= FixupTargets.size()) {
+ *E = malformedError("fixup in segment " + Twine(SegmentIndex) +
+ " at offset " + Twine(SegmentOffset) +
+ " has out-of range import ordinal " +
+ Twine(ImportOrdinal));
+ moveToEnd();
+ return;
+ }
+
+ ChainedFixupTarget &Target = FixupTargets[ImportOrdinal];
+ Ordinal = Target.libOrdinal();
+ Addend = InlineAddend ? InlineAddend : Target.addend();
+ Flags = Target.weakImport() ? MachO::BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0;
+ SymbolName = Target.symbolName();
+ } else {
+ uint64_t Target = Field(0, 36);
+ uint64_t High8 = Field(36, 8);
+
+ PointerValue = Target | (High8 << 56);
+ if (PointerFormat == MachO::DYLD_CHAINED_PTR_64_OFFSET)
+ PointerValue += textAddress();
+ }
+
+ // The stride is 4 bytes for DYLD_CHAINED_PTR_64(_OFFSET).
+ if (Next != 0) {
+ PageOffset += 4 * Next;
+ } else {
+ ++PageIndex;
+ findNextPageWithFixups();
+ }
+}
bool MachOChainedFixupEntry::operator==(
const MachOChainedFixupEntry &Other) const {
- if (Done == Other.Done)
- return true;
- if ((FixupIndex == Other.FixupIndex))
+ if (Done && Other.Done)
return true;
- return false;
+ if (Done != Other.Done)
+ return false;
+ return InfoSegIndex == Other.InfoSegIndex && PageIndex == Other.PageIndex &&
+ PageOffset == Other.PageOffset;
}
MachORebaseEntry::MachORebaseEntry(Error *E, const MachOObjectFile *O,
@@ -4302,6 +4465,9 @@ iterator_range<bind_iterator> MachOObjectFile::weakBindTable(Error &Err) {
}
iterator_range<fixup_iterator> MachOObjectFile::fixupTable(Error &Err) {
+ if (BindRebaseSectionTable == nullptr)
+ BindRebaseSectionTable = std::make_unique<BindRebaseSegInfo>(this);
+
MachOChainedFixupEntry Start(&Err, this, true);
Start.moveToFirst();
@@ -4836,13 +5002,13 @@ MachOObjectFile::getChainedFixupsHeader() const {
return CFHeader;
}
-Expected<std::pair<size_t, std::vector<MachOObjectFile::ChainedFixupsSegment>>>
+Expected<std::pair<size_t, std::vector<ChainedFixupsSegment>>>
MachOObjectFile::getChainedFixupsSegments() const {
auto CFOrErr = getChainedFixupsLoadCommand();
if (!CFOrErr)
return CFOrErr.takeError();
- std::vector<MachOObjectFile::ChainedFixupsSegment> Segments;
+ std::vector<ChainedFixupsSegment> Segments;
if (!CFOrErr->has_value())
return std::make_pair(0, Segments);
diff --git a/llvm/test/tools/llvm-objdump/MachO/dyld-info.test b/llvm/test/tools/llvm-objdump/MachO/dyld-info.test
index 85e94688ae87f..20a08b9f2c465 100644
--- a/llvm/test/tools/llvm-objdump/MachO/dyld-info.test
+++ b/llvm/test/tools/llvm-objdump/MachO/dyld-info.test
@@ -1,9 +1,22 @@
-RUN: llvm-objdump --macho --dyld-info %p/Inputs/bind.macho-x86_64 \
-RUN: | FileCheck %s --match-full-lines --strict-whitespace \
-RUN: --implicit-check-not={{.}}
-RUN: llvm-otool -dyld_info %p/Inputs/bind.macho-x86_64 \
-RUN: | FileCheck %s --match-full-lines --strict-whitespace \
-RUN: --implicit-check-not={{.}}
+RUN: llvm-objdump --macho --dyld-info %p/Inputs/chained-fixups.macho-x86_64 | \
+RUN: FileCheck -DNAME=%p/Inputs/chained-fixups.macho-x86_64 %s
+RUN: llvm-otool -dyld_info %p/Inputs/chained-fixups.macho-x86_64 | \
+RUN: FileCheck -DNAME=%p/Inputs/chained-fixups.macho-x86_64 %s
-CHECK:{{.*}}bind.macho-x86_64:
-CHECK:dyld information:
+
+## See chained-fixups.test for how the test input was generated.
+CHECK: [[NAME]]:
+CHECK-NEXT: dyld information:
+CHECK-NEXT: segment section address pointer type addend dylib symbol/vm address
+CHECK-NEXT: __DATA_CONST __const 0x3E0 0x8010000000000001 bind 0x0 libdylib _weakImport (weak import)
+CHECK-NEXT: __DATA_CONST __const 0x3E8 0x8000000000000000 bind 0x0 flat-namespace _dynamicLookup
+CHECK-NEXT: __DATA __data 0x3F0 0x00200000000003F0 rebase 0x3F0
+CHECK-NEXT: __DATA __data 0x400 0x8000000000000004 bind 0x0 weak _weak
+CHECK-NEXT: __DATA __data 0x1410 0x8000000000000003 bind 0x0 weak _weakLocal
+CHECK-NEXT: __DATA __data 0x3410 0x8010000000000002 bind 0x0 libdylib _dylib
+CHECK-NEXT: __DATA __data 0x3418 0x800000002A000002 bind 0x2A libdylib _dylib
+
+## TODO: Print opcode-based fixups/binds as well
+## For now, test that execution doesn't fail if the input uses those.
+RUN: llvm-objdump --macho --dyld-info %p/Inputs/bind.macho-x86_64
+RUN: llvm-objdump --macho --dyld-info %p/Inputs/bind.macho-x86_64
diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp
index 31bb52a795c44..c0820380fff8f 100644
--- a/llvm/tools/llvm-objdump/MachODump.cpp
+++ b/llvm/tools/llvm-objdump/MachODump.cpp
@@ -1188,15 +1188,6 @@ static void PrintLinkOptHints(MachOObjectFile *O) {
}
}
-static void printMachOChainedFixups(object::MachOObjectFile *Obj) {
- Error Err = Error::success();
- for (const object::MachOChainedFixupEntry &Entry : Obj->fixupTable(Err)) {
- (void)Entry;
- }
- if (Err)
- reportError(std::move(Err), Obj->getFileName());
-}
-
static SmallVector<std::string> GetSegmentNames(object::MachOObjectFile *O) {
SmallVector<std::string> Ret;
for (const MachOObjectFile::LoadCommandInfo &Command : O->load_commands()) {
@@ -1255,9 +1246,8 @@ static constexpr std::array<StringRef, 13> PointerFormats{
"DYLD_CHAINED_PTR_ARM64E_USERLAND24",
};
-static void
-PrintChainedFixupsSegment(const MachOObjectFile::ChainedFixupsSegment &Segment,
- StringRef SegName) {
+static void PrintChainedFixupsSegment(const ChainedFixupsSegment &Segment,
+ StringRef SegName) {
outs() << "chained starts in segment " << Segment.SegIdx << " (" << SegName
<< ")\n";
outs() << " size = " << Segment.Header.size << '\n';
@@ -1333,7 +1323,7 @@ static void PrintChainedFixups(MachOObjectFile *O) {
<< SegNames[I] << ")\n";
}
- for (const MachOObjectFile::ChainedFixupsSegment &S : Segments)
+ for (const ChainedFixupsSegment &S : Segments)
PrintChainedFixupsSegment(S, SegNames[S.SegIdx]);
auto FixupTargets =
@@ -1345,8 +1335,62 @@ static void PrintChainedFixups(MachOObjectFile *O) {
}
static void PrintDyldInfo(MachOObjectFile *O) {
- outs() << "dyld information:" << '\n';
- printMachOChainedFixups(O);
+ Error Err = Error::success();
+
+ size_t SegmentWidth = strlen("segment");
+ size_t SectionWidth = strlen("section");
+ size_t AddressWidth = strlen("address");
+ size_t AddendWidth = strlen("addend");
+ size_t DylibWidth = strlen("dylib");
+ const size_t PointerWidth = 2 + O->getBytesInAddress() * 2;
+
+ auto HexLength = [](uint64_t Num) {
+ return Num ? (size_t)divideCeil(Log2_64(Num), 4) : 1;
+ };
+ for (const object::MachOChainedFixupEntry &Entry : O->fixupTable(Err)) {
+ SegmentWidth = std::max(SegmentWidth, Entry.segmentName().size());
+ SectionWidth = std::max(SectionWidth, Entry.sectionName().size());
+ AddressWidth = std::max(AddressWidth, HexLength(Entry.address()) + 2);
+ if (Entry.isBind()) {
+ AddendWidth = std::max(AddendWidth, HexLength(Entry.addend()) + 2);
+ DylibWidth = std::max(DylibWidth, Entry.symbolName().size());
+ }
+ }
+ // Errors will be handled when printing the table.
+ if (Err)
+ consumeError(std::move(Err));
+
+ outs() << "dyld information:\n";
+ outs() << left_justify("segment", SegmentWidth) << ' '
+ << left_justify("section", SectionWidth) << ' '
+ << left_justify("address", AddressWidth) << ' '
+ << left_justify("pointer", PointerWidth) << " type "
+ << left_justify("addend", AddendWidth) << ' '
+ << left_justify("dylib", DylibWidth) << " symbol/vm address\n";
+ for (const object::MachOChainedFixupEntry &Entry : O->fixupTable(Err)) {
+ outs() << left_justify(Entry.segmentName(), SegmentWidth) << ' '
+ << left_justify(Entry.sectionName(), SectionWidth) << ' ' << "0x"
+ << left_justify(utohexstr(Entry.address()), AddressWidth - 2) << ' '
+ << format_hex(Entry.rawValue(), PointerWidth, true) << ' ';
+ if (Entry.isBind()) {
+ outs() << "bind "
+ << "0x" << left_justify(utohexstr(Entry.addend()), AddendWidth - 2)
+ << ' ' << left_justify(ordinalName(O, Entry.ordinal()), DylibWidth)
+ << ' ' << Entry.symbolName();
+ if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_WEAK_IMPORT)
+ outs() << " (weak import)";
+ outs() << '\n';
+ } else {
+ assert(Entry.isRebase());
+ outs() << "rebase";
+ outs().indent(AddendWidth + DylibWidth + 2);
+ outs() << format("0x%" PRIX64, Entry.pointerValue()) << '\n';
+ }
+ }
+ if (Err)
+ reportError(std::move(Err), O->getFileName());
+
+ // TODO: Print opcode-based fixups if the object uses those.
}
static void PrintDylibs(MachOObjectFile *O, bool JustId) {
More information about the llvm-commits
mailing list