mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 19:23:23 +01:00
Print more information about relocations.
With this patch llvm-readobj now prints if a relocation is pcrel, its length, if it is extern and if it is scattered. It also refactors the code a bit to use bit fields instead of shifts and masks all over the place. llvm-svn: 179294
This commit is contained in:
parent
3b38288ea2
commit
857bda0e10
@ -115,8 +115,23 @@ namespace MachOFormat {
|
||||
template<endianness TargetEndianness>
|
||||
struct RelocationEntry {
|
||||
LLVM_MACHOB_IMPORT_TYPES(TargetEndianness)
|
||||
MachOInt32 Word0;
|
||||
MachOInt32 Word1;
|
||||
MachOInt32 Address;
|
||||
unsigned SymbolNum : 24;
|
||||
unsigned PCRel : 1;
|
||||
unsigned Length : 2;
|
||||
unsigned External : 1;
|
||||
unsigned Type : 4;
|
||||
};
|
||||
|
||||
template<endianness TargetEndianness>
|
||||
struct ScatteredRelocationEntry {
|
||||
LLVM_MACHOB_IMPORT_TYPES(TargetEndianness)
|
||||
unsigned Address : 24;
|
||||
unsigned Type : 4;
|
||||
unsigned Length : 2;
|
||||
unsigned PCRel : 1;
|
||||
unsigned Scattered : 1;
|
||||
MachOInt32 Value;
|
||||
};
|
||||
|
||||
template<endianness TargetEndianness>
|
||||
@ -206,6 +221,8 @@ public:
|
||||
SymbolTableEntryBase;
|
||||
typedef MachOFormat::SymtabLoadCommand<support::little> SymtabLoadCommand;
|
||||
typedef MachOFormat::RelocationEntry<support::little> RelocationEntry;
|
||||
typedef MachOFormat::ScatteredRelocationEntry<support::little>
|
||||
ScatteredRelocationEntry;
|
||||
typedef MachOFormat::SectionBase SectionBase;
|
||||
typedef MachOFormat::LoadCommand<support::little> LoadCommand;
|
||||
typedef MachOFormat::Header<support::little> Header;
|
||||
@ -243,6 +260,11 @@ public:
|
||||
const Header *getHeader() const;
|
||||
unsigned getHeaderSize() const;
|
||||
StringRef getData(size_t Offset, size_t Size) const;
|
||||
const RelocationEntry *getRelocation(DataRefImpl Rel) const;
|
||||
bool isScattered(const RelocationEntry *RE) const;
|
||||
bool isPCRel(const RelocationEntry *RE) const;
|
||||
unsigned getLength(const RelocationEntry *RE) const;
|
||||
unsigned getType(const RelocationEntry *RE) const;
|
||||
|
||||
static inline bool classof(const Binary *v) {
|
||||
return v->isMachO();
|
||||
@ -479,15 +501,12 @@ MachOObjectFile<MachOT>::getRelocationAddress(DataRefImpl Rel,
|
||||
const Section *Sect = getSection(Sections[Rel.d.b]);
|
||||
uint64_t SectAddress = Sect->Address;
|
||||
const RelocationEntry *RE = getRelocation(Rel);
|
||||
unsigned Arch = getArch();
|
||||
bool isScattered = (Arch != Triple::x86_64) &&
|
||||
(RE->Word0 & macho::RF_Scattered);
|
||||
|
||||
uint64_t RelAddr;
|
||||
if (isScattered)
|
||||
RelAddr = RE->Word0 & 0xFFFFFF;
|
||||
if (isScattered(RE))
|
||||
RelAddr = RE->Address & 0xFFFFFF;
|
||||
else
|
||||
RelAddr = RE->Word0;
|
||||
RelAddr = RE->Address;
|
||||
|
||||
Res = SectAddress + RelAddr;
|
||||
return object_error::success;
|
||||
@ -498,14 +517,10 @@ error_code
|
||||
MachOObjectFile<MachOT>::getRelocationOffset(DataRefImpl Rel,
|
||||
uint64_t &Res) const {
|
||||
const RelocationEntry *RE = getRelocation(Rel);
|
||||
|
||||
unsigned Arch = getArch();
|
||||
bool isScattered = (Arch != Triple::x86_64) &&
|
||||
(RE->Word0 & macho::RF_Scattered);
|
||||
if (isScattered)
|
||||
Res = RE->Word0 & 0xFFFFFF;
|
||||
if (isScattered(RE))
|
||||
Res = RE->Address & 0xFFFFFF;
|
||||
else
|
||||
Res = RE->Word0;
|
||||
Res = RE->Address;
|
||||
return object_error::success;
|
||||
}
|
||||
|
||||
@ -514,8 +529,8 @@ error_code
|
||||
MachOObjectFile<MachOT>::getRelocationSymbol(DataRefImpl Rel,
|
||||
SymbolRef &Res) const {
|
||||
const RelocationEntry *RE = getRelocation(Rel);
|
||||
uint32_t SymbolIdx = RE->Word1 & 0xffffff;
|
||||
bool isExtern = (RE->Word1 >> 27) & 1;
|
||||
uint32_t SymbolIdx = RE->SymbolNum;
|
||||
bool isExtern = RE->External;
|
||||
|
||||
DataRefImpl Sym;
|
||||
moveToNextSymbol(Sym);
|
||||
@ -535,9 +550,7 @@ template<class MachOT>
|
||||
error_code MachOObjectFile<MachOT>::getRelocationType(DataRefImpl Rel,
|
||||
uint64_t &Res) const {
|
||||
const RelocationEntry *RE = getRelocation(Rel);
|
||||
Res = RE->Word0;
|
||||
Res <<= 32;
|
||||
Res |= RE->Word1;
|
||||
Res = getType(RE);
|
||||
return object_error::success;
|
||||
}
|
||||
|
||||
@ -550,14 +563,8 @@ MachOObjectFile<MachOT>::getRelocationTypeName(DataRefImpl Rel,
|
||||
const RelocationEntry *RE = getRelocation(Rel);
|
||||
|
||||
unsigned Arch = getArch();
|
||||
bool isScattered = (Arch != Triple::x86_64) &&
|
||||
(RE->Word0 & macho::RF_Scattered);
|
||||
|
||||
unsigned r_type;
|
||||
if (isScattered)
|
||||
r_type = (RE->Word0 >> 24) & 0xF;
|
||||
else
|
||||
r_type = (RE->Word1 >> 28) & 0xF;
|
||||
unsigned r_type = getType(RE);
|
||||
|
||||
switch (Arch) {
|
||||
case Triple::x86: {
|
||||
@ -650,30 +657,20 @@ MachOObjectFile<MachOT>::getRelocationValueString(DataRefImpl Rel,
|
||||
const RelocationEntry *RE = getRelocation(Rel);
|
||||
|
||||
unsigned Arch = getArch();
|
||||
bool isScattered = (Arch != Triple::x86_64) &&
|
||||
(RE->Word0 & macho::RF_Scattered);
|
||||
bool IsScattered = isScattered(RE);
|
||||
|
||||
std::string fmtbuf;
|
||||
raw_string_ostream fmt(fmtbuf);
|
||||
|
||||
unsigned Type;
|
||||
if (isScattered)
|
||||
Type = (RE->Word0 >> 24) & 0xF;
|
||||
else
|
||||
Type = (RE->Word1 >> 28) & 0xF;
|
||||
|
||||
bool isPCRel;
|
||||
if (isScattered)
|
||||
isPCRel = ((RE->Word0 >> 30) & 1);
|
||||
else
|
||||
isPCRel = ((RE->Word1 >> 24) & 1);
|
||||
unsigned Type = getType(RE);
|
||||
bool IsPCRel = isPCRel(RE);
|
||||
|
||||
// Determine any addends that should be displayed with the relocation.
|
||||
// These require decoding the relocation type, which is triple-specific.
|
||||
|
||||
// X86_64 has entirely custom relocation types.
|
||||
if (Arch == Triple::x86_64) {
|
||||
bool isPCRel = ((RE->Word1 >> 24) & 1);
|
||||
bool isPCRel = RE->PCRel;
|
||||
|
||||
switch (Type) {
|
||||
case macho::RIT_X86_64_GOTLoad: // X86_64_RELOC_GOT_LOAD
|
||||
@ -691,7 +688,7 @@ MachOObjectFile<MachOT>::getRelocationValueString(DataRefImpl Rel,
|
||||
// X86_64_SUBTRACTOR must be followed by a relocation of type
|
||||
// X86_64_RELOC_UNSIGNED.
|
||||
// NOTE: Scattered relocations don't exist on x86_64.
|
||||
unsigned RType = (RENext->Word1 >> 28) & 0xF;
|
||||
unsigned RType = RENext->Type;
|
||||
if (RType != 0)
|
||||
report_fatal_error("Expected X86_64_RELOC_UNSIGNED after "
|
||||
"X86_64_RELOC_SUBTRACTOR.");
|
||||
@ -738,12 +735,12 @@ MachOObjectFile<MachOT>::getRelocationValueString(DataRefImpl Rel,
|
||||
// X86 sect diff's must be followed by a relocation of type
|
||||
// GENERIC_RELOC_PAIR.
|
||||
bool isNextScattered = (Arch != Triple::x86_64) &&
|
||||
(RENext->Word0 & macho::RF_Scattered);
|
||||
(RENext->Address & macho::RF_Scattered);
|
||||
unsigned RType;
|
||||
if (isNextScattered)
|
||||
RType = (RENext->Word0 >> 24) & 0xF;
|
||||
RType = (RENext->Address >> 24) & 0xF;
|
||||
else
|
||||
RType = (RENext->Word1 >> 28) & 0xF;
|
||||
RType = RENext->Type;
|
||||
if (RType != 1)
|
||||
report_fatal_error("Expected GENERIC_RELOC_PAIR after "
|
||||
"GENERIC_RELOC_SECTDIFF.");
|
||||
@ -767,12 +764,12 @@ MachOObjectFile<MachOT>::getRelocationValueString(DataRefImpl Rel,
|
||||
// X86 sect diff's must be followed by a relocation of type
|
||||
// GENERIC_RELOC_PAIR.
|
||||
bool isNextScattered = (Arch != Triple::x86_64) &&
|
||||
(RENext->Word0 & macho::RF_Scattered);
|
||||
(RENext->Address & macho::RF_Scattered);
|
||||
unsigned RType;
|
||||
if (isNextScattered)
|
||||
RType = (RENext->Word0 >> 24) & 0xF;
|
||||
RType = (RENext->Address >> 24) & 0xF;
|
||||
else
|
||||
RType = (RENext->Word1 >> 28) & 0xF;
|
||||
RType = RENext->Type;
|
||||
if (RType != 1)
|
||||
report_fatal_error("Expected GENERIC_RELOC_PAIR after "
|
||||
"GENERIC_RELOC_LOCAL_SECTDIFF.");
|
||||
@ -785,7 +782,7 @@ MachOObjectFile<MachOT>::getRelocationValueString(DataRefImpl Rel,
|
||||
case macho::RIT_Generic_TLV: {
|
||||
printRelocationTargetName(RE, fmt);
|
||||
fmt << "@TLV";
|
||||
if (isPCRel) fmt << "P";
|
||||
if (IsPCRel) fmt << "P";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -798,10 +795,10 @@ MachOObjectFile<MachOT>::getRelocationValueString(DataRefImpl Rel,
|
||||
// Half relocations steal a bit from the length field to encode
|
||||
// whether this is an upper16 or a lower16 relocation.
|
||||
bool isUpper;
|
||||
if (isScattered)
|
||||
isUpper = (RE->Word0 >> 28) & 1;
|
||||
if (IsScattered)
|
||||
isUpper = (RE->Address >> 28) & 1;
|
||||
else
|
||||
isUpper = (RE->Word1 >> 25) & 1;
|
||||
isUpper = (RE->Length >> 1) & 1;
|
||||
|
||||
if (isUpper)
|
||||
fmt << ":upper16:(";
|
||||
@ -816,12 +813,12 @@ MachOObjectFile<MachOT>::getRelocationValueString(DataRefImpl Rel,
|
||||
// ARM half relocs must be followed by a relocation of type
|
||||
// ARM_RELOC_PAIR.
|
||||
bool isNextScattered = (Arch != Triple::x86_64) &&
|
||||
(RENext->Word0 & macho::RF_Scattered);
|
||||
(RENext->Address & macho::RF_Scattered);
|
||||
unsigned RType;
|
||||
if (isNextScattered)
|
||||
RType = (RENext->Word0 >> 24) & 0xF;
|
||||
RType = (RENext->Address >> 24) & 0xF;
|
||||
else
|
||||
RType = (RENext->Word1 >> 28) & 0xF;
|
||||
RType = RENext->Type;
|
||||
|
||||
if (RType != 1)
|
||||
report_fatal_error("Expected ARM_RELOC_PAIR after "
|
||||
@ -860,15 +857,8 @@ error_code
|
||||
MachOObjectFile<MachOT>::getRelocationHidden(DataRefImpl Rel,
|
||||
bool &Result) const {
|
||||
const RelocationEntry *RE = getRelocation(Rel);
|
||||
|
||||
unsigned Arch = getArch();
|
||||
bool isScattered = (Arch != Triple::x86_64) &&
|
||||
(RE->Word0 & macho::RF_Scattered);
|
||||
unsigned Type;
|
||||
if (isScattered)
|
||||
Type = (RE->Word0 >> 24) & 0xF;
|
||||
else
|
||||
Type = (RE->Word1 >> 28) & 0xF;
|
||||
unsigned Type = getType(RE);
|
||||
|
||||
Result = false;
|
||||
|
||||
@ -884,7 +874,7 @@ MachOObjectFile<MachOT>::getRelocationHidden(DataRefImpl Rel,
|
||||
RelPrev.d.a--;
|
||||
const RelocationEntry *REPrev = getRelocation(RelPrev);
|
||||
|
||||
unsigned PrevType = (REPrev->Word1 >> 28) & 0xF;
|
||||
unsigned PrevType = REPrev->Type;
|
||||
|
||||
if (PrevType == macho::RIT_X86_64_Subtractor) Result = true;
|
||||
}
|
||||
|
@ -80,6 +80,46 @@ StringRef MachOObjectFileBase::getData(size_t Offset, size_t Size) const {
|
||||
return ObjectFile::getData().substr(Offset, Size);
|
||||
}
|
||||
|
||||
const MachOObjectFileBase::RelocationEntry *
|
||||
MachOObjectFileBase::getRelocation(DataRefImpl Rel) const {
|
||||
if (const MachOObjectFile32Le *O = dyn_cast<MachOObjectFile32Le>(this))
|
||||
return O->getRelocation(Rel);
|
||||
const MachOObjectFile64Le *O = dyn_cast<MachOObjectFile64Le>(this);
|
||||
return O->getRelocation(Rel);
|
||||
}
|
||||
|
||||
bool MachOObjectFileBase::isScattered(const RelocationEntry *RE) const {
|
||||
unsigned Arch = getArch();
|
||||
return (Arch != Triple::x86_64) && (RE->Address & macho::RF_Scattered);
|
||||
}
|
||||
|
||||
bool MachOObjectFileBase::isPCRel(const RelocationEntry *RE) const {
|
||||
if (isScattered(RE)) {
|
||||
const ScatteredRelocationEntry *SRE =
|
||||
reinterpret_cast<const ScatteredRelocationEntry *>(RE);
|
||||
return SRE->PCRel;
|
||||
}
|
||||
return RE->PCRel;
|
||||
}
|
||||
|
||||
unsigned MachOObjectFileBase::getLength(const RelocationEntry *RE) const {
|
||||
if (isScattered(RE)) {
|
||||
const ScatteredRelocationEntry *SRE =
|
||||
reinterpret_cast<const ScatteredRelocationEntry *>(RE);
|
||||
return SRE->Length;
|
||||
}
|
||||
return RE->Length;
|
||||
}
|
||||
|
||||
unsigned MachOObjectFileBase::getType(const RelocationEntry *RE) const {
|
||||
if (isScattered(RE)) {
|
||||
const ScatteredRelocationEntry *SRE =
|
||||
reinterpret_cast<const ScatteredRelocationEntry *>(RE);
|
||||
return SRE->Type;
|
||||
}
|
||||
return RE->Type;
|
||||
}
|
||||
|
||||
ObjectFile *ObjectFile::createMachOObjectFile(MemoryBuffer *Buffer) {
|
||||
StringRef Magic = Buffer->getBuffer().slice(0, 4);
|
||||
error_code ec;
|
||||
@ -435,16 +475,12 @@ void advanceTo(T &it, size_t Val) {
|
||||
void
|
||||
MachOObjectFileBase::printRelocationTargetName(const RelocationEntry *RE,
|
||||
raw_string_ostream &fmt) const {
|
||||
unsigned Arch = getArch();
|
||||
bool isScattered = (Arch != Triple::x86_64) &&
|
||||
(RE->Word0 & macho::RF_Scattered);
|
||||
|
||||
// Target of a scattered relocation is an address. In the interest of
|
||||
// generating pretty output, scan through the symbol table looking for a
|
||||
// symbol that aligns with that address. If we find one, print it.
|
||||
// Otherwise, we just print the hex address of the target.
|
||||
if (isScattered) {
|
||||
uint32_t Val = RE->Word1;
|
||||
if (isScattered(RE)) {
|
||||
uint32_t Val = RE->SymbolNum;
|
||||
|
||||
error_code ec;
|
||||
for (symbol_iterator SI = begin_symbols(), SE = end_symbols(); SI != SE;
|
||||
@ -486,8 +522,8 @@ MachOObjectFileBase::printRelocationTargetName(const RelocationEntry *RE,
|
||||
}
|
||||
|
||||
StringRef S;
|
||||
bool isExtern = (RE->Word1 >> 27) & 1;
|
||||
uint32_t Val = RE->Word1 & 0xFFFFFF;
|
||||
bool isExtern = RE->External;
|
||||
uint32_t Val = RE->Address;
|
||||
|
||||
if (isExtern) {
|
||||
symbol_iterator SI = begin_symbols();
|
||||
|
@ -26,17 +26,17 @@ ELF-NEXT: ]
|
||||
|
||||
MACHO-I386: Relocations [
|
||||
MACHO-I386-NEXT: Section __text {
|
||||
MACHO-I386-NEXT: 0x18 GENERIC_RELOC_VANILLA _SomeOtherFunction 0x0
|
||||
MACHO-I386-NEXT: 0x13 GENERIC_RELOC_VANILLA _puts 0x0
|
||||
MACHO-I386-NEXT: 0xB GENERIC_RELOC_LOCAL_SECTDIFF _main 0x{{[0-9A-F]+}}
|
||||
MACHO-I386-NEXT: 0x0 GENERIC_RELOC_PAIR _main 0x{{[0-9A-F]+}}
|
||||
MACHO-I386-NEXT: 0x18 1 2 1 GENERIC_RELOC_VANILLA 0 _SomeOtherFunction
|
||||
MACHO-I386-NEXT: 0x13 1 2 1 GENERIC_RELOC_VANILLA 0 _puts
|
||||
MACHO-I386-NEXT: 0xB 0 2 n/a GENERIC_RELOC_LOCAL_SECTDIFF 1 _main
|
||||
MACHO-I386-NEXT: 0x0 0 2 n/a GENERIC_RELOC_PAIR 1 _main
|
||||
MACHO-I386-NEXT: }
|
||||
MACHO-I386-NEXT: ]
|
||||
|
||||
MACHO-X86-64: Relocations [
|
||||
MACHO-X86-64-NEXT: Section __text {
|
||||
MACHO-X86-64-NEXT: 0xE X86_64_RELOC_BRANCH _SomeOtherFunction 0x0
|
||||
MACHO-X86-64-NEXT: 0x9 X86_64_RELOC_BRANCH _puts 0x0
|
||||
MACHO-X86-64-NEXT: 0x4 X86_64_RELOC_SIGNED L_.str 0x0
|
||||
MACHO-X86-64-NEXT: 0xE 1 2 1 X86_64_RELOC_BRANCH 0 _SomeOtherFunction
|
||||
MACHO-X86-64-NEXT: 0x9 1 2 1 X86_64_RELOC_BRANCH 0 _puts
|
||||
MACHO-X86-64-NEXT: 0x4 1 2 1 X86_64_RELOC_SIGNED 0 L_.str
|
||||
MACHO-X86-64-NEXT: }
|
||||
MACHO-X86-64-NEXT:]
|
||||
|
@ -153,10 +153,10 @@ MACHO-I386-NEXT: ]
|
||||
MACHO-I386-NEXT: Reserved1: 0x0
|
||||
MACHO-I386-NEXT: Reserved2: 0x0
|
||||
MACHO-I386-NEXT: Relocations [
|
||||
MACHO-I386-NEXT: 0x18 GENERIC_RELOC_VANILLA _SomeOtherFunction 0x0
|
||||
MACHO-I386-NEXT: 0x13 GENERIC_RELOC_VANILLA _puts 0x0
|
||||
MACHO-I386-NEXT: 0xB GENERIC_RELOC_LOCAL_SECTDIFF _main 0x{{[0-9A-F]+}}
|
||||
MACHO-I386-NEXT: 0x0 GENERIC_RELOC_PAIR _main 0x{{[0-9A-F]+}}
|
||||
MACHO-I386-NEXT: 0x18 1 2 1 GENERIC_RELOC_VANILLA 0 _SomeOtherFunction
|
||||
MACHO-I386-NEXT: 0x13 1 2 1 GENERIC_RELOC_VANILLA 0 _puts
|
||||
MACHO-I386-NEXT: 0xB 0 2 n/a GENERIC_RELOC_LOCAL_SECTDIFF 1 _main
|
||||
MACHO-I386-NEXT: 0x0 0 2 n/a GENERIC_RELOC_PAIR 1 _main
|
||||
MACHO-I386-NEXT: ]
|
||||
MACHO-I386-NEXT: Symbols [
|
||||
MACHO-I386-NEXT: Symbol {
|
||||
@ -196,9 +196,9 @@ MACHO-X86-64-NEXT: ]
|
||||
MACHO-X86-64-NEXT: Reserved1: 0x0
|
||||
MACHO-X86-64-NEXT: Reserved2: 0x0
|
||||
MACHO-X86-64-NEXT: Relocations [
|
||||
MACHO-X86-64-NEXT: 0xE X86_64_RELOC_BRANCH _SomeOtherFunction 0x0
|
||||
MACHO-X86-64-NEXT: 0x9 X86_64_RELOC_BRANCH _puts 0x0
|
||||
MACHO-X86-64-NEXT: 0x4 X86_64_RELOC_SIGNED L_.str 0x0
|
||||
MACHO-X86-64-NEXT: 0xE 1 2 1 X86_64_RELOC_BRANCH 0 _SomeOtherFunction
|
||||
MACHO-X86-64-NEXT: 0x9 1 2 1 X86_64_RELOC_BRANCH 0 _puts
|
||||
MACHO-X86-64-NEXT: 0x4 1 2 1 X86_64_RELOC_SIGNED 0 L_.str
|
||||
MACHO-X86-64-NEXT: ]
|
||||
MACHO-X86-64-NEXT: Symbols [
|
||||
MACHO-X86-64-NEXT: Symbol {
|
||||
|
@ -330,20 +330,28 @@ void MachODumper::printRelocation(section_iterator SecI,
|
||||
relocation_iterator RelI) {
|
||||
uint64_t Offset;
|
||||
SmallString<32> RelocName;
|
||||
int64_t Info;
|
||||
StringRef SymbolName;
|
||||
SymbolRef Symbol;
|
||||
if (error(RelI->getOffset(Offset))) return;
|
||||
if (error(RelI->getTypeName(RelocName))) return;
|
||||
if (error(RelI->getAdditionalInfo(Info))) return;
|
||||
if (error(RelI->getSymbol(Symbol))) return;
|
||||
if (error(Symbol.getName(SymbolName))) return;
|
||||
|
||||
DataRefImpl DR = RelI->getRawDataRefImpl();
|
||||
const MachOObjectFileBase::RelocationEntry *RE = Obj->getRelocation(DR);
|
||||
bool IsScattered = Obj->isScattered(RE);
|
||||
|
||||
raw_ostream& OS = W.startLine();
|
||||
OS << W.hex(Offset)
|
||||
<< " " << RelocName
|
||||
<< " " << Obj->isPCRel(RE)
|
||||
<< " " << Obj->getLength(RE);
|
||||
if (IsScattered)
|
||||
OS << " n/a";
|
||||
else
|
||||
OS << " " << RE->External;
|
||||
OS << " " << RelocName
|
||||
<< " " << IsScattered
|
||||
<< " " << (SymbolName.size() > 0 ? SymbolName : "-")
|
||||
<< " " << W.hex(Info)
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user