mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
Add the printing the Mach-O (__LLVM,__bundle) xar archive file section "verbosely"
to llvm-objdump. This section is created with -fembed-bitcode option. This requires the use of libxar and the Cmake and lit support were crafted by Chris Bieneman! rdar://26202242 llvm-svn: 270491
This commit is contained in:
parent
3b7fd697f3
commit
b9ab4d056f
@ -141,6 +141,11 @@ if( NOT PURE_WINDOWS AND NOT LLVM_USE_SANITIZER MATCHES "Memory.*")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
check_library_exists(xar xar_open "" HAVE_LIBXAR)
|
||||
if(HAVE_LIBXAR)
|
||||
set(XAR_LIB xar)
|
||||
endif()
|
||||
|
||||
# function checks
|
||||
check_symbol_exists(arc4random "stdlib.h" HAVE_DECL_ARC4RANDOM)
|
||||
check_symbol_exists(backtrace "execinfo.h" HAVE_BACKTRACE)
|
||||
|
@ -322,6 +322,9 @@
|
||||
/* Define if the setupterm() function is supported this platform. */
|
||||
#cmakedefine HAVE_TERMINFO ${HAVE_TERMINFO}
|
||||
|
||||
/* Define if the xar_open() function is supported this platform. */
|
||||
#cmakedefine HAVE_LIBXAR ${HAVE_LIBXAR}
|
||||
|
||||
/* Define to 1 if you have the <termios.h> header file. */
|
||||
#cmakedefine HAVE_TERMIOS_H ${HAVE_TERMIOS_H}
|
||||
|
||||
|
@ -21,4 +21,6 @@ add_llvm_library(LLVMObject
|
||||
|
||||
DEPENDS
|
||||
intrinsics_gen
|
||||
|
||||
LINK_LIBS ${XAR_LIB}
|
||||
)
|
||||
|
@ -497,3 +497,6 @@ except OSError:
|
||||
if re.search(r'ON', llvm_config_cmd.stdout.read().decode('ascii')):
|
||||
config.available_features.add('global-isel')
|
||||
llvm_config_cmd.wait()
|
||||
|
||||
if config.have_libxar:
|
||||
config.available_features.add('xar')
|
||||
|
@ -34,6 +34,7 @@ config.host_ldflags = "@HOST_LDFLAGS@"
|
||||
config.llvm_use_intel_jitevents = "@LLVM_USE_INTEL_JITEVENTS@"
|
||||
config.llvm_use_sanitizer = "@LLVM_USE_SANITIZER@"
|
||||
config.have_zlib = "@HAVE_LIBZ@"
|
||||
config.have_libxar = "@HAVE_LIBXAR@"
|
||||
config.have_dia_sdk = @HAVE_DIA_SDK@
|
||||
config.enable_ffi = "@LLVM_ENABLE_FFI@"
|
||||
config.test_examples = "@ENABLE_EXAMPLES@"
|
||||
|
BIN
test/tools/llvm-objdump/Inputs/LLVM-bundle.macho-x86_64
Executable file
BIN
test/tools/llvm-objdump/Inputs/LLVM-bundle.macho-x86_64
Executable file
Binary file not shown.
60
test/tools/llvm-objdump/macho-LLVM-bundle.test
Normal file
60
test/tools/llvm-objdump/macho-LLVM-bundle.test
Normal file
@ -0,0 +1,60 @@
|
||||
# REQUIRES: xar
|
||||
# RUN: llvm-objdump -macho -archive-headers -section __LLVM,__bundle %p/Inputs/LLVM-bundle.macho-x86_64 | FileCheck %s
|
||||
|
||||
# CHECK: For (__LLVM,__bundle) section: xar header
|
||||
# CHECK: magic XAR_HEADER_MAGIC
|
||||
# CHECK: size 28
|
||||
# CHECK: version 1
|
||||
# CHECK: toc_length_compressed 542
|
||||
# CHECK: toc_length_uncompressed 1250
|
||||
# CHECK: cksum_alg XAR_CKSUM_SHA1
|
||||
# CHECK: For (__LLVM,__bundle) section: xar archive files:
|
||||
# CHECK: 1664 1
|
||||
# CHECK: For (__LLVM,__bundle) section: xar table of contents:
|
||||
# CHECK: <?xml version="1.0" encoding="UTF-8"?>
|
||||
# CHECK: <xar>
|
||||
# CHECK: <subdoc subdoc_name="Ld">
|
||||
# CHECK: <version>1.0</version>
|
||||
# CHECK: <architecture>x86_64</architecture>
|
||||
# CHECK: <platform>MacOSX</platform>
|
||||
# CHECK: <sdkversion>10.11.0</sdkversion>
|
||||
# CHECK: <dylibs>
|
||||
# CHECK: <lib>libSystem.dylib</lib>
|
||||
# CHECK: </dylibs>
|
||||
# CHECK: <link-options>
|
||||
# CHECK: <option>-execute</option>
|
||||
# CHECK: <option>-macosx_version_min</option>
|
||||
# CHECK: <option>10.11.0</option>
|
||||
# CHECK: <option>-e</option>
|
||||
# CHECK: <option>_main</option>
|
||||
# CHECK: <option>-executable_path</option>
|
||||
# CHECK: <option>hello</option>
|
||||
# CHECK: </link-options>
|
||||
# CHECK: </subdoc>
|
||||
# CHECK: <toc>
|
||||
# CHECK: <checksum style="sha1">
|
||||
# CHECK: <size>20</size>
|
||||
# CHECK: <offset>0</offset>
|
||||
# CHECK: </checksum>
|
||||
# CHECK: <creation-time>2016-05-23T20:49:10</creation-time>
|
||||
# CHECK: <file id="1">
|
||||
# CHECK: <name>1</name>
|
||||
# CHECK: <type>file</type>
|
||||
# CHECK: <data>
|
||||
# CHECK: <archived-checksum style="sha1">a319940ff5f5248ca8b44cf7b4b65e7dd49a47ab</archived-checksum>
|
||||
# CHECK: <extracted-checksum style="sha1">a319940ff5f5248ca8b44cf7b4b65e7dd49a47ab</extracted-checksum>
|
||||
# CHECK: <size>1664</size>
|
||||
# CHECK: <offset>20</offset>
|
||||
# CHECK: <encoding style="application/octet-stream"/>
|
||||
# CHECK: <length>1664</length>
|
||||
# CHECK: </data>
|
||||
# CHECK: <file-type>Bitcode</file-type>
|
||||
# CHECK: <clang>
|
||||
# CHECK: <cmd>-triple</cmd>
|
||||
# CHECK: <cmd>x86_64-apple-macosx10.11.0</cmd>
|
||||
# CHECK: <cmd>-emit-obj</cmd>
|
||||
# CHECK: <cmd>-disable-llvm-optzns</cmd>
|
||||
# CHECK: </clang>
|
||||
# CHECK: </file>
|
||||
# CHECK: </toc>
|
||||
# CHECK: </xar>
|
@ -42,6 +42,7 @@
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Support/ToolOutputFile.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
@ -51,6 +52,12 @@
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBXAR
|
||||
extern "C" {
|
||||
#include <xar/xar.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
using namespace llvm;
|
||||
using namespace object;
|
||||
|
||||
@ -1041,6 +1048,12 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
|
||||
StringRef DisSegName, StringRef DisSectName);
|
||||
static void DumpProtocolSection(MachOObjectFile *O, const char *sect,
|
||||
uint32_t size, uint32_t addr);
|
||||
#ifdef HAVE_LIBXAR
|
||||
static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
|
||||
uint32_t size, bool verbose,
|
||||
bool PrintXarHeader, bool PrintXarFileHeaders,
|
||||
std::string XarMemberName);
|
||||
#endif // defined(HAVE_LIBXAR)
|
||||
|
||||
static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
|
||||
bool verbose) {
|
||||
@ -1102,6 +1115,13 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
|
||||
DumpProtocolSection(O, sect, sect_size, sect_addr);
|
||||
continue;
|
||||
}
|
||||
#ifdef HAVE_LIBXAR
|
||||
if (SegName == "__LLVM" && SectName == "__bundle") {
|
||||
DumpBitcodeSection(O, sect, sect_size, verbose, !NoSymbolicOperands,
|
||||
ArchiveHeaders, "");
|
||||
continue;
|
||||
}
|
||||
#endif // defined(HAVE_LIBXAR)
|
||||
switch (section_type) {
|
||||
case MachO::S_REGULAR:
|
||||
DumpRawSectionContents(O, sect, sect_size, sect_addr);
|
||||
@ -5645,6 +5665,369 @@ static void DumpProtocolSection(MachOObjectFile *O, const char *sect,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBXAR
|
||||
inline void swapStruct(struct xar_header &xar) {
|
||||
sys::swapByteOrder(xar.magic);
|
||||
sys::swapByteOrder(xar.size);
|
||||
sys::swapByteOrder(xar.version);
|
||||
sys::swapByteOrder(xar.toc_length_compressed);
|
||||
sys::swapByteOrder(xar.toc_length_uncompressed);
|
||||
sys::swapByteOrder(xar.cksum_alg);
|
||||
}
|
||||
|
||||
static void PrintModeVerbose(uint32_t mode) {
|
||||
switch(mode & S_IFMT){
|
||||
case S_IFDIR:
|
||||
outs() << "d";
|
||||
break;
|
||||
case S_IFCHR:
|
||||
outs() << "c";
|
||||
break;
|
||||
case S_IFBLK:
|
||||
outs() << "b";
|
||||
break;
|
||||
case S_IFREG:
|
||||
outs() << "-";
|
||||
break;
|
||||
case S_IFLNK:
|
||||
outs() << "l";
|
||||
break;
|
||||
case S_IFSOCK:
|
||||
outs() << "s";
|
||||
break;
|
||||
default:
|
||||
outs() << "?";
|
||||
break;
|
||||
}
|
||||
|
||||
/* owner permissions */
|
||||
if(mode & S_IREAD)
|
||||
outs() << "r";
|
||||
else
|
||||
outs() << "-";
|
||||
if(mode & S_IWRITE)
|
||||
outs() << "w";
|
||||
else
|
||||
outs() << "-";
|
||||
if(mode & S_ISUID)
|
||||
outs() << "s";
|
||||
else if(mode & S_IEXEC)
|
||||
outs() << "x";
|
||||
else
|
||||
outs() << "-";
|
||||
|
||||
/* group permissions */
|
||||
if(mode & (S_IREAD >> 3))
|
||||
outs() << "r";
|
||||
else
|
||||
outs() << "-";
|
||||
if(mode & (S_IWRITE >> 3))
|
||||
outs() << "w";
|
||||
else
|
||||
outs() << "-";
|
||||
if(mode & S_ISGID)
|
||||
outs() << "s";
|
||||
else if(mode & (S_IEXEC >> 3))
|
||||
outs() << "x";
|
||||
else
|
||||
outs() << "-";
|
||||
|
||||
/* other permissions */
|
||||
if(mode & (S_IREAD >> 6))
|
||||
outs() << "r";
|
||||
else
|
||||
outs() << "-";
|
||||
if(mode & (S_IWRITE >> 6))
|
||||
outs() << "w";
|
||||
else
|
||||
outs() << "-";
|
||||
if(mode & S_ISVTX)
|
||||
outs() << "t";
|
||||
else if(mode & (S_IEXEC >> 6))
|
||||
outs() << "x";
|
||||
else
|
||||
outs() << "-";
|
||||
}
|
||||
|
||||
static void PrintXarFilesSummary(const char *XarFilename, xar_t xar) {
|
||||
xar_iter_t xi;
|
||||
xar_file_t xf;
|
||||
xar_iter_t xp;
|
||||
const char *key, *type, *mode, *user, *group, *size, *mtime, *name, *m;
|
||||
char *endp;
|
||||
uint32_t mode_value;
|
||||
|
||||
xi = xar_iter_new();
|
||||
if (!xi) {
|
||||
errs() << "Can't obtain an xar iterator for xar archive "
|
||||
<< XarFilename << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Go through the xar's files.
|
||||
for (xf = xar_file_first(xar, xi); xf; xf = xar_file_next(xi)) {
|
||||
xp = xar_iter_new();
|
||||
if(!xp){
|
||||
errs() << "Can't obtain an xar iterator for xar archive "
|
||||
<< XarFilename << "\n";
|
||||
return;
|
||||
}
|
||||
type = nullptr;
|
||||
mode = nullptr;
|
||||
user = nullptr;
|
||||
group = nullptr;
|
||||
size = nullptr;
|
||||
mtime = nullptr;
|
||||
name = nullptr;
|
||||
for(key = xar_prop_first(xf, xp); key; key = xar_prop_next(xp)){
|
||||
const char *val = nullptr;
|
||||
xar_prop_get(xf, key, &val);
|
||||
#if 0 // Useful for debugging.
|
||||
outs() << "key: " << key << " value: " << val << "\n";
|
||||
#endif
|
||||
if(strcmp(key, "type") == 0)
|
||||
type = val;
|
||||
if(strcmp(key, "mode") == 0)
|
||||
mode = val;
|
||||
if(strcmp(key, "user") == 0)
|
||||
user = val;
|
||||
if(strcmp(key, "group") == 0)
|
||||
group = val;
|
||||
if(strcmp(key, "data/size") == 0)
|
||||
size = val;
|
||||
if(strcmp(key, "mtime") == 0)
|
||||
mtime = val;
|
||||
if(strcmp(key, "name") == 0)
|
||||
name = val;
|
||||
}
|
||||
if(mode != nullptr){
|
||||
mode_value = strtoul(mode, &endp, 8);
|
||||
if(*endp != '\0')
|
||||
outs() << "(mode: \"" << mode << "\" contains non-octal chars) ";
|
||||
if(strcmp(type, "file") == 0)
|
||||
mode_value |= S_IFREG;
|
||||
PrintModeVerbose(mode_value);
|
||||
outs() << " ";
|
||||
}
|
||||
if(user != nullptr)
|
||||
outs() << format("%10s/", user);
|
||||
if(group != nullptr)
|
||||
outs() << format("%-10s ", group);
|
||||
if(size != nullptr)
|
||||
outs() << format("%7s ", size);
|
||||
if(mtime != nullptr){
|
||||
for(m = mtime; *m != 'T' && *m != '\0'; m++)
|
||||
outs() << *m;
|
||||
if(*m == 'T')
|
||||
m++;
|
||||
outs() << " ";
|
||||
for( ; *m != 'Z' && *m != '\0'; m++)
|
||||
outs() << *m;
|
||||
outs() << " ";
|
||||
}
|
||||
if(name != nullptr)
|
||||
outs() << name;
|
||||
outs() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpBitcodeSection(MachOObjectFile *O, const char *sect,
|
||||
uint32_t size, bool verbose,
|
||||
bool PrintXarHeader, bool PrintXarFileHeaders,
|
||||
std::string XarMemberName) {
|
||||
if(size < sizeof(struct xar_header)) {
|
||||
outs() << "size of (__LLVM,__bundle) section too small (smaller than size "
|
||||
"of struct xar_header)\n";
|
||||
return;
|
||||
}
|
||||
struct xar_header XarHeader;
|
||||
memcpy(&XarHeader, sect, sizeof(struct xar_header));
|
||||
if (sys::IsLittleEndianHost)
|
||||
swapStruct(XarHeader);
|
||||
if (PrintXarHeader) {
|
||||
if (!XarMemberName.empty())
|
||||
outs() << "In xar member " << XarMemberName << ": ";
|
||||
else
|
||||
outs() << "For (__LLVM,__bundle) section: ";
|
||||
outs() << "xar header\n";
|
||||
if (XarHeader.magic == XAR_HEADER_MAGIC)
|
||||
outs() << " magic XAR_HEADER_MAGIC\n";
|
||||
else
|
||||
outs() << " magic "
|
||||
<< format_hex(XarHeader.magic, 10, true)
|
||||
<< " (not XAR_HEADER_MAGIC)\n";
|
||||
outs() << " size " << XarHeader.size << "\n";
|
||||
outs() << " version " << XarHeader.version << "\n";
|
||||
outs() << " toc_length_compressed " << XarHeader.toc_length_compressed
|
||||
<< "\n";
|
||||
outs() << "toc_length_uncompressed " << XarHeader.toc_length_uncompressed
|
||||
<< "\n";
|
||||
outs() << " cksum_alg ";
|
||||
switch (XarHeader.cksum_alg) {
|
||||
case XAR_CKSUM_NONE:
|
||||
outs() << "XAR_CKSUM_NONE\n";
|
||||
break;
|
||||
case XAR_CKSUM_SHA1:
|
||||
outs() << "XAR_CKSUM_SHA1\n";
|
||||
break;
|
||||
case XAR_CKSUM_MD5:
|
||||
outs() << "XAR_CKSUM_MD5\n";
|
||||
break;
|
||||
case XAR_CKSUM_SHA256:
|
||||
outs() << "XAR_CKSUM_SHA256\n";
|
||||
break;
|
||||
case XAR_CKSUM_SHA512:
|
||||
outs() << "XAR_CKSUM_SHA512\n";
|
||||
break;
|
||||
default:
|
||||
outs() << XarHeader.cksum_alg << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
SmallString<128> XarFilename;
|
||||
int FD;
|
||||
std::error_code XarEC =
|
||||
sys::fs::createTemporaryFile("llvm-objdump", "xar", FD, XarFilename);
|
||||
if (XarEC) {
|
||||
errs() << XarEC.message() << "\n";
|
||||
return;
|
||||
}
|
||||
tool_output_file XarFile(XarFilename, FD);
|
||||
raw_fd_ostream &XarOut = XarFile.os();
|
||||
StringRef XarContents(sect, size);
|
||||
XarOut << XarContents;
|
||||
XarOut.close();
|
||||
if (XarOut.has_error())
|
||||
return;
|
||||
|
||||
xar_t xar = xar_open(XarFilename.c_str(), READ);
|
||||
if (!xar) {
|
||||
errs() << "Can't create temporary xar archive " << XarFilename << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
SmallString<128> TocFilename;
|
||||
std::error_code TocEC =
|
||||
sys::fs::createTemporaryFile("llvm-objdump", "toc", TocFilename);
|
||||
if (TocEC) {
|
||||
errs() << TocEC.message() << "\n";
|
||||
return;
|
||||
}
|
||||
xar_serialize(xar, TocFilename.c_str());
|
||||
|
||||
if (PrintXarFileHeaders) {
|
||||
if (!XarMemberName.empty())
|
||||
outs() << "In xar member " << XarMemberName << ": ";
|
||||
else
|
||||
outs() << "For (__LLVM,__bundle) section: ";
|
||||
outs() << "xar archive files:\n";
|
||||
PrintXarFilesSummary(XarFilename.c_str(), xar);
|
||||
}
|
||||
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
|
||||
MemoryBuffer::getFileOrSTDIN(TocFilename.c_str());
|
||||
if (std::error_code EC = FileOrErr.getError()) {
|
||||
errs() << EC.message() << "\n";
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
|
||||
|
||||
if (!XarMemberName.empty())
|
||||
outs() << "In xar member " << XarMemberName << ": ";
|
||||
else
|
||||
outs() << "For (__LLVM,__bundle) section: ";
|
||||
outs() << "xar table of contents:\n";
|
||||
outs() << Buffer->getBuffer() << "\n";
|
||||
|
||||
// TODO: Go through the xar's files.
|
||||
xar_iter_t xi = xar_iter_new();
|
||||
if(!xi){
|
||||
errs() << "Can't obtain an xar iterator for xar archive "
|
||||
<< XarFilename.c_str() << "\n";
|
||||
xar_close(xar);
|
||||
return;
|
||||
}
|
||||
for(xar_file_t xf = xar_file_first(xar, xi); xf; xf = xar_file_next(xi)){
|
||||
const char *key;
|
||||
xar_iter_t xp;
|
||||
const char *member_name, *member_type, *member_size_string;
|
||||
size_t member_size;
|
||||
|
||||
xp = xar_iter_new();
|
||||
if(!xp){
|
||||
errs() << "Can't obtain an xar iterator for xar archive "
|
||||
<< XarFilename.c_str() << "\n";
|
||||
xar_close(xar);
|
||||
return;
|
||||
}
|
||||
member_name = NULL;
|
||||
member_type = NULL;
|
||||
member_size_string = NULL;
|
||||
for(key = xar_prop_first(xf, xp); key; key = xar_prop_next(xp)){
|
||||
const char *val = nullptr;
|
||||
xar_prop_get(xf, key, &val);
|
||||
#if 0 // Useful for debugging.
|
||||
outs() << "key: " << key << " value: " << val << "\n";
|
||||
#endif
|
||||
if(strcmp(key, "name") == 0)
|
||||
member_name = val;
|
||||
if(strcmp(key, "type") == 0)
|
||||
member_type = val;
|
||||
if(strcmp(key, "data/size") == 0)
|
||||
member_size_string = val;
|
||||
}
|
||||
/*
|
||||
* If we find a file with a name, date/size and type properties
|
||||
* and with the type being "file" see if that is a xar file.
|
||||
*/
|
||||
if (member_name != NULL && member_type != NULL &&
|
||||
strcmp(member_type, "file") == 0 &&
|
||||
member_size_string != NULL){
|
||||
// Extract the file into a buffer.
|
||||
char *endptr;
|
||||
member_size = strtoul(member_size_string, &endptr, 10);
|
||||
if (*endptr == '\0' && member_size != 0) {
|
||||
char *buffer = (char *) ::operator new (member_size);
|
||||
if (xar_extract_tobuffersz(xar, xf, &buffer, &member_size) == 0) {
|
||||
#if 0 // Useful for debugging.
|
||||
outs() << "xar member: " << member_name << " extracted\n";
|
||||
#endif
|
||||
// Set the XarMemberName we want to see printed in the header.
|
||||
std::string OldXarMemberName;
|
||||
// If XarMemberName is already set this is nested. So
|
||||
// save the old name and create the nested name.
|
||||
if (!XarMemberName.empty()) {
|
||||
OldXarMemberName = XarMemberName;
|
||||
XarMemberName =
|
||||
(Twine("[") + XarMemberName + "]" + member_name).str();
|
||||
} else {
|
||||
OldXarMemberName = "";
|
||||
XarMemberName = member_name;
|
||||
}
|
||||
// See if this is could be a xar file (nested).
|
||||
if (member_size >= sizeof(struct xar_header)) {
|
||||
#if 0 // Useful for debugging.
|
||||
outs() << "could be a xar file: " << member_name << "\n";
|
||||
#endif
|
||||
memcpy((char *)&XarHeader, buffer, sizeof(struct xar_header));
|
||||
if (sys::IsLittleEndianHost)
|
||||
swapStruct(XarHeader);
|
||||
if(XarHeader.magic == XAR_HEADER_MAGIC)
|
||||
DumpBitcodeSection(O, buffer, member_size, verbose,
|
||||
PrintXarHeader, PrintXarFileHeaders,
|
||||
XarMemberName);
|
||||
}
|
||||
XarMemberName = OldXarMemberName;
|
||||
}
|
||||
delete buffer;
|
||||
}
|
||||
}
|
||||
xar_iter_free(xp);
|
||||
}
|
||||
xar_close(xar);
|
||||
}
|
||||
#endif // defined(HAVE_LIBXAR)
|
||||
|
||||
static void printObjcMetaData(MachOObjectFile *O, bool verbose) {
|
||||
if (O->is64Bit())
|
||||
printObjc2_64bit_MetaData(O, verbose);
|
||||
|
Loading…
Reference in New Issue
Block a user