mirror of
https://github.com/darlinghq/darling-gdb.git
synced 2024-11-24 12:39:59 +00:00
Add Elf_file interface which can be used by both Sized_relobj and
Sized_dynobj.
This commit is contained in:
parent
9e59254c41
commit
645f81233b
@ -570,6 +570,12 @@ class Ehdr
|
||||
: p_(reinterpret_cast<const internal::Ehdr_data<size>*>(p))
|
||||
{ }
|
||||
|
||||
template<typename File>
|
||||
Ehdr(File* file, typename File::Location loc)
|
||||
: p_(reinterpret_cast<const internal::Ehdr_data<size>*>(
|
||||
file->view(loc.file_offset, loc.data_size).data()))
|
||||
{ }
|
||||
|
||||
const unsigned char*
|
||||
get_e_ident() const
|
||||
{ return this->p_->e_ident; }
|
||||
@ -710,6 +716,12 @@ class Shdr
|
||||
: p_(reinterpret_cast<const internal::Shdr_data<size>*>(p))
|
||||
{ }
|
||||
|
||||
template<typename File>
|
||||
Shdr(File* file, typename File::Location loc)
|
||||
: p_(reinterpret_cast<const internal::Shdr_data<size>*>(
|
||||
file->view(loc.file_offset, loc.data_size).data()))
|
||||
{ }
|
||||
|
||||
Elf_Word
|
||||
get_sh_name() const
|
||||
{ return Convert<32, big_endian>::convert_host(this->p_->sh_name); }
|
||||
@ -819,6 +831,12 @@ class Phdr
|
||||
: p_(reinterpret_cast<const internal::Phdr_data<size>*>(p))
|
||||
{ }
|
||||
|
||||
template<typename File>
|
||||
Phdr(File* file, typename File::Location loc)
|
||||
: p_(reinterpret_cast<internal::Phdr_data<size>*>(
|
||||
file->view(loc.file_offset, loc.data_size).data()))
|
||||
{ }
|
||||
|
||||
Elf_Word
|
||||
get_p_type() const
|
||||
{ return Convert<32, big_endian>::convert_host(this->p_->p_type); }
|
||||
@ -911,6 +929,12 @@ class Sym
|
||||
: p_(reinterpret_cast<const internal::Sym_data<size>*>(p))
|
||||
{ }
|
||||
|
||||
template<typename File>
|
||||
Sym(File* file, typename File::Location loc)
|
||||
: p_(reinterpret_cast<const internal::Sym_data<size>*>(
|
||||
file->view(loc.file_offset, loc.data_size).data()))
|
||||
{ }
|
||||
|
||||
Elf_Word
|
||||
get_st_name() const
|
||||
{ return Convert<32, big_endian>::convert_host(this->p_->st_name); }
|
||||
@ -1015,6 +1039,12 @@ class Rel
|
||||
: p_(reinterpret_cast<const internal::Rel_data<size>*>(p))
|
||||
{ }
|
||||
|
||||
template<typename File>
|
||||
Rel(File* file, typename File::Location loc)
|
||||
: p_(reinterpret_cast<const internal::Rel_data<size>*>(
|
||||
file->view(loc.file_offset, loc.data_size).data()))
|
||||
{ }
|
||||
|
||||
typename Elf_types<size>::Elf_Addr
|
||||
get_r_offset() const
|
||||
{ return Convert<size, big_endian>::convert_host(this->p_->r_offset); }
|
||||
@ -1035,6 +1065,12 @@ class Rela
|
||||
: p_(reinterpret_cast<const internal::Rela_data<size>*>(p))
|
||||
{ }
|
||||
|
||||
template<typename File>
|
||||
Rela(File* file, typename File::Location loc)
|
||||
: p_(reinterpret_cast<const internal::Rela_data<size>*>(
|
||||
file->view(loc.file_offset, loc.data_size).data()))
|
||||
{ }
|
||||
|
||||
typename Elf_types<size>::Elf_Addr
|
||||
get_r_offset() const
|
||||
{ return Convert<size, big_endian>::convert_host(this->p_->r_offset); }
|
||||
|
255
elfcpp/elfcpp_file.h
Normal file
255
elfcpp/elfcpp_file.h
Normal file
@ -0,0 +1,255 @@
|
||||
// elfcpp_file.h -- file access for elfcpp -*- C++ -*-
|
||||
|
||||
// This header file defines the class Elf_file which can be used to
|
||||
// read useful data from an ELF file. The functions here are all
|
||||
// templates which take a file interface object as a parameter. This
|
||||
// type must have a subtype View. This type must support two methods:
|
||||
// View view(off_t file_offset, off_t data_size)
|
||||
// returns a View for the specified part of the file.
|
||||
// void error(const char* printf_format, ...)
|
||||
// prints an error message and does not return. The subtype View must
|
||||
// support a method
|
||||
// const unsigned char* data()
|
||||
// which returns a pointer to a buffer containing the requested data.
|
||||
// This general interface is used to read data from the file. Objects
|
||||
// of type View will never survive longer than the elfcpp function.
|
||||
|
||||
// Some of these functions must return a reference to part of the
|
||||
// file. To use these, the file interface must support a subtype
|
||||
// Location:
|
||||
// Location(off_t file_offset, off_t data_size)
|
||||
// To use this in conjunction with the accessors types Shdr, etc., the
|
||||
// file interface should support an overload of view:
|
||||
// View view(Location)
|
||||
// This permits writing
|
||||
// elfcpp::Shdr shdr(file, ef.section_header(n));
|
||||
|
||||
#ifndef ELFPCP_FILE_H
|
||||
#define ELFCPP_FILE_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
namespace elfcpp
|
||||
{
|
||||
|
||||
// This object is used to read an ELF file.
|
||||
// SIZE: The size of file, 32 or 64.
|
||||
// BIG_ENDIAN: Whether the file is in big-endian format.
|
||||
// FILE: A file reading type as described above.
|
||||
|
||||
template<int size, bool big_endian, typename File>
|
||||
class Elf_file
|
||||
{
|
||||
private:
|
||||
typedef Elf_file<size, big_endian, File> This;
|
||||
|
||||
public:
|
||||
static const int ehdr_size = Elf_sizes<size>::ehdr_size;
|
||||
static const int phdr_size = Elf_sizes<size>::phdr_size;
|
||||
static const int shdr_size = Elf_sizes<size>::shdr_size;
|
||||
static const int sym_size = Elf_sizes<size>::sym_size;
|
||||
static const int rel_size = Elf_sizes<size>::rel_size;
|
||||
static const int rela_size = Elf_sizes<size>::rela_size;
|
||||
|
||||
typedef Ehdr<size, big_endian> Ef_ehdr;
|
||||
typedef Phdr<size, big_endian> Ef_phdr;
|
||||
typedef Shdr<size, big_endian> Ef_shdr;
|
||||
typedef Sym<size, big_endian> Ef_sym;
|
||||
|
||||
// Construct an Elf_file given an ELF file header.
|
||||
Elf_file(File* file, const Ef_ehdr& ehdr)
|
||||
{ this->construct(file, ehdr); }
|
||||
|
||||
// Construct an ELF file.
|
||||
inline
|
||||
Elf_file(File* file);
|
||||
|
||||
// Return the file offset to the section headers.
|
||||
off_t
|
||||
shoff() const
|
||||
{ return this->shoff_; }
|
||||
|
||||
// Return the number of sections.
|
||||
unsigned int
|
||||
shnum()
|
||||
{
|
||||
this->initialize_shnum();
|
||||
return this->shnum_;
|
||||
}
|
||||
|
||||
// Return the section index of the section name string table.
|
||||
unsigned int
|
||||
shstrndx()
|
||||
{
|
||||
this->initialize_shnum();
|
||||
return this->shstrndx_;
|
||||
}
|
||||
|
||||
// Return the location of the header of section SHNDX.
|
||||
typename File::Location
|
||||
section_header(unsigned int shndx)
|
||||
{
|
||||
return typename File::Location(this->section_header_offset(shndx),
|
||||
shdr_size);
|
||||
}
|
||||
|
||||
// Return the name of section SHNDX.
|
||||
std::string
|
||||
section_name(unsigned int shndx);
|
||||
|
||||
// Return the location of the contents of section SHNDX.
|
||||
typename File::Location
|
||||
section_contents(unsigned int shndx);
|
||||
|
||||
private:
|
||||
// Shared constructor code.
|
||||
void
|
||||
construct(File* file, const Ef_ehdr& ehdr);
|
||||
|
||||
// Initialize shnum_ and shstrndx_.
|
||||
void
|
||||
initialize_shnum();
|
||||
|
||||
// Return the file offset of the header of section SHNDX.
|
||||
off_t
|
||||
section_header_offset(unsigned int shndx);
|
||||
|
||||
// The file we are reading.
|
||||
File* file_;
|
||||
// The file offset to the section headers.
|
||||
off_t shoff_;
|
||||
// The number of sections.
|
||||
unsigned int shnum_;
|
||||
// The section index of the section name string table.
|
||||
unsigned int shstrndx_;
|
||||
};
|
||||
|
||||
// Template function definitions.
|
||||
|
||||
// Construct an Elf_file given an ELF file header.
|
||||
|
||||
template<int size, bool big_endian, typename File>
|
||||
void
|
||||
Elf_file<size, big_endian, File>::construct(File* file, const Ef_ehdr& ehdr)
|
||||
{
|
||||
this->file_ = file;
|
||||
this->shoff_ = ehdr.get_e_shoff();
|
||||
this->shnum_ = ehdr.get_e_shnum();
|
||||
this->shstrndx_ = ehdr.get_e_shstrndx();
|
||||
if (ehdr.get_e_ehsize() != This::ehdr_size)
|
||||
file->error(_("bad e_ehsize (%d != %d)"),
|
||||
ehdr.get_e_ehsize(), This::ehdr_size);
|
||||
if (ehdr.get_e_shentsize() != This::shdr_size)
|
||||
file->error(_("bad e_shentsize (%d != %d)"),
|
||||
ehdr.get_e_shentsize(), This::shdr_size);
|
||||
}
|
||||
|
||||
// Construct an ELF file.
|
||||
|
||||
template<int size, bool big_endian, typename File>
|
||||
inline
|
||||
Elf_file<size, big_endian, File>::Elf_file(File* file)
|
||||
{
|
||||
typename File::View v(file->view(file_header_offset, This::ehdr_size));
|
||||
this->construct(file, Ef_ehdr(v.data()));
|
||||
}
|
||||
|
||||
// Initialize the shnum_ and shstrndx_ fields, handling overflow.
|
||||
|
||||
template<int size, bool big_endian, typename File>
|
||||
void
|
||||
Elf_file<size, big_endian, File>::initialize_shnum()
|
||||
{
|
||||
if ((this->shnum_ == 0 || this->shstrndx_ == SHN_XINDEX)
|
||||
&& this->shoff_ != 0)
|
||||
{
|
||||
typename File::View v(this->file_->view(this->shoff_, This::shdr_size));
|
||||
Ef_shdr shdr(v.data());
|
||||
if (this->shnum_ == 0)
|
||||
this->shnum_ = shdr.get_sh_size();
|
||||
if (this->shstrndx_ == SHN_XINDEX)
|
||||
this->shstrndx_ = shdr.get_sh_link();
|
||||
}
|
||||
}
|
||||
|
||||
// Return the file offset of the section header of section SHNDX.
|
||||
|
||||
template<int size, bool big_endian, typename File>
|
||||
off_t
|
||||
Elf_file<size, big_endian, File>::section_header_offset(unsigned int shndx)
|
||||
{
|
||||
if (shndx >= this->shnum())
|
||||
this->file_->error(_("section_header_offset: bad shndx %u >= %u"),
|
||||
shndx, this->shnum());
|
||||
return this->shoff_ + This::shdr_size * shndx;
|
||||
}
|
||||
|
||||
// Return the name of section SHNDX.
|
||||
|
||||
template<int size, bool big_endian, typename File>
|
||||
std::string
|
||||
Elf_file<size, big_endian, File>::section_name(unsigned int shndx)
|
||||
{
|
||||
File* const file = this->file_;
|
||||
|
||||
// Get the section name offset.
|
||||
unsigned int sh_name;
|
||||
{
|
||||
typename File::View v(file->view(this->section_header_offset(shndx),
|
||||
This::shdr_size));
|
||||
Ef_shdr shdr(v.data());
|
||||
sh_name = shdr.get_sh_name();
|
||||
}
|
||||
|
||||
// Get the file offset for the section name string table data.
|
||||
off_t shstr_off;
|
||||
off_t shstr_size;
|
||||
{
|
||||
const unsigned int shstrndx = this->shstrndx_;
|
||||
typename File::View v(file->view(this->section_header_offset(shstrndx),
|
||||
This::shdr_size));
|
||||
Ef_shdr shstr_shdr(v.data());
|
||||
shstr_off = shstr_shdr.get_sh_offset();
|
||||
shstr_size = shstr_shdr.get_sh_size();
|
||||
}
|
||||
|
||||
if (sh_name >= shstr_size)
|
||||
file->error(_("bad section name offset for section %u: %u"),
|
||||
shndx, sh_name);
|
||||
|
||||
typename File::View v(file->view(shstr_off, shstr_size));
|
||||
|
||||
const unsigned char* datau = v.data();
|
||||
const char* data = reinterpret_cast<const char*>(datau);
|
||||
const void* p = ::memchr(data + sh_name, '\0', shstr_size - sh_name);
|
||||
if (p == NULL)
|
||||
file->error(_("missing null terminator for name of section %u"),
|
||||
shndx);
|
||||
|
||||
size_t len = static_cast<const char*>(p) - (data + sh_name);
|
||||
|
||||
return std::string(data + sh_name, len);
|
||||
}
|
||||
|
||||
// Return the contents of section SHNDX.
|
||||
|
||||
template<int size, bool big_endian, typename File>
|
||||
typename File::Location
|
||||
Elf_file<size, big_endian, File>::section_contents(unsigned int shndx)
|
||||
{
|
||||
File* const file = this->file_;
|
||||
|
||||
if (shndx >= this->shnum())
|
||||
file->error(_("section_contents: bad shndx %u >= %u"),
|
||||
shndx, this->shnum());
|
||||
|
||||
typename File::View v(file->view(this->section_header_offset(shndx),
|
||||
This::shdr_size));
|
||||
Ef_shdr shdr(v.data());
|
||||
return typename File::Location(shdr.get_sh_offset(), shdr.get_sh_size());
|
||||
}
|
||||
|
||||
} // End namespace elfcpp.
|
||||
|
||||
#endif // !defined(ELFCPP_FILE_H)
|
227
gold/object.cc
227
gold/object.cc
@ -5,6 +5,7 @@
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <cstdarg>
|
||||
|
||||
#include "target-select.h"
|
||||
#include "layout.h"
|
||||
@ -16,6 +17,34 @@
|
||||
namespace gold
|
||||
{
|
||||
|
||||
// Class Object.
|
||||
|
||||
// Report an error for the elfcpp::Elf_file interface.
|
||||
|
||||
void
|
||||
Object::error(const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
fprintf(stderr, "%s: %s: ", program_name, this->name().c_str());
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
putc('\n', stderr);
|
||||
|
||||
gold_exit(false);
|
||||
}
|
||||
|
||||
// Return a view of the contents of a section.
|
||||
|
||||
const unsigned char*
|
||||
Object::section_contents(unsigned int shndx, off_t* plen)
|
||||
{
|
||||
Location loc(this->do_section_contents(shndx));
|
||||
*plen = loc.data_size;
|
||||
return this->get_view(loc.file_offset, loc.data_size);
|
||||
}
|
||||
|
||||
// Class Sized_relobj.
|
||||
|
||||
template<int size, bool big_endian>
|
||||
@ -25,31 +54,15 @@ Sized_relobj<size, big_endian>::Sized_relobj(
|
||||
off_t offset,
|
||||
const elfcpp::Ehdr<size, big_endian>& ehdr)
|
||||
: Relobj(name, input_file, offset),
|
||||
elf_file_(this, ehdr),
|
||||
section_headers_(NULL),
|
||||
flags_(ehdr.get_e_flags()),
|
||||
shoff_(ehdr.get_e_shoff()),
|
||||
shstrndx_(0),
|
||||
symtab_shnum_(0),
|
||||
symtab_shndx_(0),
|
||||
local_symbol_count_(0),
|
||||
output_local_symbol_count_(0),
|
||||
symbols_(NULL),
|
||||
local_symbol_offset_(0),
|
||||
values_(NULL)
|
||||
{
|
||||
if (ehdr.get_e_ehsize() != This::ehdr_size)
|
||||
{
|
||||
fprintf(stderr, _("%s: %s: bad e_ehsize field (%d != %d)\n"),
|
||||
program_name, this->name().c_str(), ehdr.get_e_ehsize(),
|
||||
This::ehdr_size);
|
||||
gold_exit(false);
|
||||
}
|
||||
if (ehdr.get_e_shentsize() != This::shdr_size)
|
||||
{
|
||||
fprintf(stderr, _("%s: %s: bad e_shentsize field (%d != %d)\n"),
|
||||
program_name, this->name().c_str(), ehdr.get_e_shentsize(),
|
||||
This::shdr_size);
|
||||
gold_exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
template<int size, bool big_endian>
|
||||
@ -57,59 +70,7 @@ Sized_relobj<size, big_endian>::~Sized_relobj()
|
||||
{
|
||||
}
|
||||
|
||||
// Read the section header for section SHNUM.
|
||||
|
||||
template<int size, bool big_endian>
|
||||
inline const unsigned char*
|
||||
Sized_relobj<size, big_endian>::section_header(unsigned int shnum)
|
||||
{
|
||||
assert(shnum < this->shnum());
|
||||
off_t symtabshdroff = this->shoff_ + shnum * This::shdr_size;
|
||||
return this->get_view(symtabshdroff, This::shdr_size);
|
||||
}
|
||||
|
||||
// Return the name of section SHNUM. The object must already be
|
||||
// locked.
|
||||
|
||||
template<int size, bool big_endian>
|
||||
std::string
|
||||
Sized_relobj<size, big_endian>::do_section_name(unsigned int shnum)
|
||||
{
|
||||
// Read the section names.
|
||||
typename This::Shdr shdrnames(this->section_header(this->shstrndx_));
|
||||
const unsigned char* pnamesu = this->get_view(shdrnames.get_sh_offset(),
|
||||
shdrnames.get_sh_size());
|
||||
const char* pnames = reinterpret_cast<const char*>(pnamesu);
|
||||
|
||||
typename This::Shdr shdr(this->section_header(shnum));
|
||||
if (shdr.get_sh_name() >= shdrnames.get_sh_size())
|
||||
{
|
||||
fprintf(stderr,
|
||||
_("%s: %s: bad section name offset for section %u: %lu\n"),
|
||||
program_name, this->name().c_str(), shnum,
|
||||
static_cast<unsigned long>(shdr.get_sh_name()));
|
||||
gold_exit(false);
|
||||
}
|
||||
|
||||
return std::string(pnames + shdr.get_sh_name());
|
||||
}
|
||||
|
||||
// Return a view of the contents of section SHNUM. The object does
|
||||
// not have to be locked.
|
||||
|
||||
template<int size, bool big_endian>
|
||||
const unsigned char*
|
||||
Sized_relobj<size, big_endian>::do_section_contents(unsigned int shnum,
|
||||
off_t* plen)
|
||||
{
|
||||
Task_locker_obj<Object> tl(*this);
|
||||
|
||||
typename This::Shdr shdr(this->section_header(shnum));
|
||||
*plen = shdr.get_sh_size();
|
||||
return this->get_view(shdr.get_sh_offset(), shdr.get_sh_size());
|
||||
}
|
||||
|
||||
// Set up an object file bsaed on the file header. This sets up the
|
||||
// Set up an object file based on the file header. This sets up the
|
||||
// target and reads the section information.
|
||||
|
||||
template<int size, bool big_endian>
|
||||
@ -129,25 +90,14 @@ Sized_relobj<size, big_endian>::setup(
|
||||
}
|
||||
this->set_target(target);
|
||||
|
||||
unsigned int shnum = ehdr.get_e_shnum();
|
||||
unsigned int shstrndx = ehdr.get_e_shstrndx();
|
||||
if ((shnum == 0 || shstrndx == elfcpp::SHN_XINDEX)
|
||||
&& this->shoff_ != 0)
|
||||
{
|
||||
typename This::Shdr shdr(this->section_header(0));
|
||||
if (shnum == 0)
|
||||
shnum = shdr.get_sh_size();
|
||||
if (shstrndx == elfcpp::SHN_XINDEX)
|
||||
shstrndx = shdr.get_sh_link();
|
||||
}
|
||||
unsigned int shnum = this->elf_file_.shnum();
|
||||
this->set_shnum(shnum);
|
||||
this->shstrndx_ = shstrndx;
|
||||
|
||||
if (shnum == 0)
|
||||
return;
|
||||
|
||||
// We store the section headers in a File_view until do_read_symbols.
|
||||
this->section_headers_ = this->get_lasting_view(this->shoff_,
|
||||
off_t shoff = this->elf_file_.shoff();
|
||||
this->section_headers_ = this->get_lasting_view(shoff,
|
||||
shnum * This::shdr_size);
|
||||
|
||||
// Find the SHT_SYMTAB section. The ELF standard says that maybe in
|
||||
@ -163,7 +113,7 @@ Sized_relobj<size, big_endian>::setup(
|
||||
typename This::Shdr shdr(p);
|
||||
if (shdr.get_sh_type() == elfcpp::SHT_SYMTAB)
|
||||
{
|
||||
this->symtab_shnum_ = i;
|
||||
this->symtab_shndx_ = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -181,13 +131,15 @@ Sized_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
|
||||
|
||||
// Read the section names.
|
||||
const unsigned char* pshdrs = sd->section_headers->data();
|
||||
const unsigned char* pshdrnames = pshdrs + this->shstrndx_ * This::shdr_size;
|
||||
const unsigned char* pshdrnames = (pshdrs
|
||||
+ (this->elf_file_.shstrndx()
|
||||
* This::shdr_size));
|
||||
typename This::Shdr shdrnames(pshdrnames);
|
||||
sd->section_names_size = shdrnames.get_sh_size();
|
||||
sd->section_names = this->get_lasting_view(shdrnames.get_sh_offset(),
|
||||
sd->section_names_size);
|
||||
|
||||
if (this->symtab_shnum_ == 0)
|
||||
if (this->symtab_shndx_ == 0)
|
||||
{
|
||||
// No symbol table. Weird but legal.
|
||||
sd->symbols = NULL;
|
||||
@ -199,7 +151,7 @@ Sized_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
|
||||
|
||||
// Get the symbol table section header.
|
||||
typename This::Shdr symtabshdr(pshdrs
|
||||
+ this->symtab_shnum_ * This::shdr_size);
|
||||
+ this->symtab_shndx_ * This::shdr_size);
|
||||
assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
|
||||
|
||||
// We only need the external symbols.
|
||||
@ -276,14 +228,8 @@ Sized_relobj<size, big_endian>::include_section_group(
|
||||
|
||||
// Get the appropriate symbol table header (this will normally be
|
||||
// the single SHT_SYMTAB section, but in principle it need not be).
|
||||
if (shdr.get_sh_link() >= this->shnum())
|
||||
{
|
||||
fprintf(stderr, _("%s: %s: section group %u link %u out of range\n"),
|
||||
program_name, this->name().c_str(), index, shdr.get_sh_link());
|
||||
gold_exit(false);
|
||||
}
|
||||
|
||||
typename This::Shdr symshdr(this->section_header(shdr.get_sh_link()));
|
||||
const unsigned int link = shdr.get_sh_link();
|
||||
typename This::Shdr symshdr(this, this->elf_file_.section_header(link));
|
||||
|
||||
// Read the symbol table entry.
|
||||
if (shdr.get_sh_info() >= symshdr.get_sh_size() / This::sym_size)
|
||||
@ -296,24 +242,14 @@ Sized_relobj<size, big_endian>::include_section_group(
|
||||
const unsigned char* psym = this->get_view(symoff, This::sym_size);
|
||||
elfcpp::Sym<size, big_endian> sym(psym);
|
||||
|
||||
// Read the section header for the symbol table names.
|
||||
if (symshdr.get_sh_link() >= this->shnum())
|
||||
{
|
||||
fprintf(stderr, _("%s; %s: symtab section %u link %u out of range\n"),
|
||||
program_name, this->name().c_str(), shdr.get_sh_link(),
|
||||
symshdr.get_sh_link());
|
||||
gold_exit(false);
|
||||
}
|
||||
|
||||
typename This::Shdr symnamehdr(this->section_header(symshdr.get_sh_link()));
|
||||
|
||||
// Read the symbol table names.
|
||||
const unsigned char *psymnamesu = this->get_view(symnamehdr.get_sh_offset(),
|
||||
symnamehdr.get_sh_size());
|
||||
off_t symnamelen;
|
||||
const unsigned char* psymnamesu;
|
||||
psymnamesu = this->section_contents(symshdr.get_sh_link(), &symnamelen);
|
||||
const char* psymnames = reinterpret_cast<const char*>(psymnamesu);
|
||||
|
||||
// Get the section group signature.
|
||||
if (sym.get_st_name() >= symnamehdr.get_sh_size())
|
||||
if (sym.get_st_name() >= symnamelen)
|
||||
{
|
||||
fprintf(stderr, _("%s: %s: symbol %u name offset %u out of range\n"),
|
||||
program_name, this->name().c_str(), shdr.get_sh_info(),
|
||||
@ -327,26 +263,11 @@ Sized_relobj<size, big_endian>::include_section_group(
|
||||
// associated with a section symbol, and then fail to give a name to
|
||||
// the section symbol. In such a case, use the name of the section.
|
||||
// FIXME.
|
||||
if (signature[0] == '\0'
|
||||
&& sym.get_st_type() == elfcpp::STT_SECTION
|
||||
&& sym.get_st_shndx() < this->shnum())
|
||||
std::string secname;
|
||||
if (signature[0] == '\0' && sym.get_st_type() == elfcpp::STT_SECTION)
|
||||
{
|
||||
typename This::Shdr shdrnames(this->section_header(this->shstrndx_));
|
||||
const unsigned char* pnamesu = this->get_view(shdrnames.get_sh_offset(),
|
||||
shdrnames.get_sh_size());
|
||||
const char* pnames = reinterpret_cast<const char*>(pnamesu);
|
||||
|
||||
typename This::Shdr sechdr(this->section_header(sym.get_st_shndx()));
|
||||
if (sechdr.get_sh_name() >= shdrnames.get_sh_size())
|
||||
{
|
||||
fprintf(stderr,
|
||||
_("%s: %s: bad section name offset for section %u: %lu\n"),
|
||||
program_name, this->name().c_str(), sym.get_st_shndx(),
|
||||
static_cast<unsigned long>(sechdr.get_sh_name()));
|
||||
gold_exit(false);
|
||||
}
|
||||
|
||||
signature = pnames + sechdr.get_sh_name();
|
||||
secname = this->section_name(sym.get_st_shndx());
|
||||
signature = secname.c_str();
|
||||
}
|
||||
|
||||
// Record this section group, and see whether we've already seen one
|
||||
@ -543,7 +464,7 @@ off_t
|
||||
Sized_relobj<size, big_endian>::do_finalize_local_symbols(off_t off,
|
||||
Stringpool* pool)
|
||||
{
|
||||
if (this->symtab_shnum_ == 0)
|
||||
if (this->symtab_shndx_ == 0)
|
||||
{
|
||||
// This object has no symbols. Weird but legal.
|
||||
return off;
|
||||
@ -554,7 +475,9 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(off_t off,
|
||||
this->local_symbol_offset_ = off;
|
||||
|
||||
// Read the symbol table section header.
|
||||
typename This::Shdr symtabshdr(this->section_header(this->symtab_shnum_));
|
||||
const unsigned int symtab_shndx = this->symtab_shndx_;
|
||||
typename This::Shdr symtabshdr(this,
|
||||
this->elf_file_.section_header(symtab_shndx));
|
||||
assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
|
||||
|
||||
// Read the local symbols.
|
||||
@ -567,14 +490,11 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(off_t off,
|
||||
|
||||
this->values_ = new typename elfcpp::Elf_types<size>::Elf_Addr[loccount];
|
||||
|
||||
// Read the section header for the symbol names.
|
||||
typename This::Shdr strtabshdr(
|
||||
this->section_header(symtabshdr.get_sh_link()));
|
||||
assert(strtabshdr.get_sh_type() == elfcpp::SHT_STRTAB);
|
||||
|
||||
// Read the symbol names.
|
||||
const unsigned char* pnamesu = this->get_view(strtabshdr.get_sh_offset(),
|
||||
strtabshdr.get_sh_size());
|
||||
const unsigned int strtab_shndx = symtabshdr.get_sh_link();
|
||||
off_t strtab_size;
|
||||
const unsigned char* pnamesu = this->section_contents(strtab_shndx,
|
||||
&strtab_size);
|
||||
const char* pnames = reinterpret_cast<const char*>(pnamesu);
|
||||
|
||||
// Loop over the local symbols.
|
||||
@ -628,6 +548,17 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(off_t off,
|
||||
|
||||
if (sym.get_st_type() != elfcpp::STT_SECTION)
|
||||
{
|
||||
if (sym.get_st_name() >= strtab_size)
|
||||
{
|
||||
fprintf(stderr,
|
||||
_("%s: %s: local symbol %u section name "
|
||||
"out of range: %u >= %u\n"),
|
||||
program_name, this->name().c_str(),
|
||||
i, sym.get_st_name(),
|
||||
static_cast<unsigned int>(strtab_size));
|
||||
gold_exit(false);
|
||||
}
|
||||
|
||||
pool->add(pnames + sym.get_st_name(), NULL);
|
||||
off += sym_size;
|
||||
++count;
|
||||
@ -646,14 +577,16 @@ void
|
||||
Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of,
|
||||
const Stringpool* sympool)
|
||||
{
|
||||
if (this->symtab_shnum_ == 0)
|
||||
if (this->symtab_shndx_ == 0)
|
||||
{
|
||||
// This object has no symbols. Weird but legal.
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the symbol table section header.
|
||||
typename This::Shdr symtabshdr(this->section_header(this->symtab_shnum_));
|
||||
const unsigned int symtab_shndx = this->symtab_shndx_;
|
||||
typename This::Shdr symtabshdr(this,
|
||||
this->elf_file_.section_header(symtab_shndx));
|
||||
assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
|
||||
const unsigned int loccount = this->local_symbol_count_;
|
||||
assert(loccount == symtabshdr.get_sh_info());
|
||||
@ -664,14 +597,11 @@ Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of,
|
||||
const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
|
||||
locsize);
|
||||
|
||||
// Read the section header for the symbol names.
|
||||
typename This::Shdr strtabshdr(
|
||||
this->section_header(symtabshdr.get_sh_link()));
|
||||
assert(strtabshdr.get_sh_type() == elfcpp::SHT_STRTAB);
|
||||
|
||||
// Read the symbol names.
|
||||
const unsigned char* pnamesu = this->get_view(strtabshdr.get_sh_offset(),
|
||||
strtabshdr.get_sh_size());
|
||||
const unsigned int strtab_shndx = symtabshdr.get_sh_link();
|
||||
off_t strtab_size;
|
||||
const unsigned char* pnamesu = this->section_contents(strtab_shndx,
|
||||
&strtab_size);
|
||||
const char* pnames = reinterpret_cast<const char*>(pnamesu);
|
||||
|
||||
// Get a view into the output file.
|
||||
@ -701,6 +631,7 @@ Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of,
|
||||
|
||||
elfcpp::Sym_write<size, big_endian> osym(ov);
|
||||
|
||||
assert(isym.get_st_name() < strtab_size);
|
||||
osym.put_st_name(sympool->get_offset(pnames + isym.get_st_name()));
|
||||
osym.put_st_value(this->values_[i]);
|
||||
osym.put_st_size(isym.get_st_size());
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "elfcpp.h"
|
||||
#include "elfcpp_file.h"
|
||||
#include "fileread.h"
|
||||
#include "target.h"
|
||||
|
||||
@ -147,8 +148,7 @@ class Object
|
||||
// Return a view of the contents of a section. Set *PLEN to the
|
||||
// size.
|
||||
const unsigned char*
|
||||
section_contents(unsigned int shnum, off_t* plen)
|
||||
{ return this->do_section_contents(shnum, plen); }
|
||||
section_contents(unsigned int shndx, off_t* plen);
|
||||
|
||||
// Return the name of a section given a section index. This is only
|
||||
// used for error messages.
|
||||
@ -156,6 +156,52 @@ class Object
|
||||
section_name(unsigned int shnum)
|
||||
{ return this->do_section_name(shnum); }
|
||||
|
||||
// Functions and types for the elfcpp::Elf_file interface. This
|
||||
// permit us to use Object as the File template parameter for
|
||||
// elfcpp::Elf_file.
|
||||
|
||||
// The View class is returned by view. It must support a single
|
||||
// method, data(). This is trivial, because get_view does what we
|
||||
// need.
|
||||
class View
|
||||
{
|
||||
public:
|
||||
View(const unsigned char* p)
|
||||
: p_(p)
|
||||
{ }
|
||||
|
||||
const unsigned char*
|
||||
data() const
|
||||
{ return this->p_; }
|
||||
|
||||
private:
|
||||
const unsigned char* p_;
|
||||
};
|
||||
|
||||
// Return a View.
|
||||
View
|
||||
view(off_t file_offset, off_t data_size)
|
||||
{ return View(this->get_view(file_offset, data_size)); }
|
||||
|
||||
// Report an error.
|
||||
void
|
||||
error(const char* format, ...) ATTRIBUTE_PRINTF_2;
|
||||
|
||||
// A location in the file.
|
||||
struct Location
|
||||
{
|
||||
off_t file_offset;
|
||||
off_t data_size;
|
||||
|
||||
Location(off_t fo, off_t ds)
|
||||
: file_offset(fo), data_size(ds)
|
||||
{ }
|
||||
};
|
||||
|
||||
// Get a View given a Location.
|
||||
View view(Location loc)
|
||||
{ return View(this->get_view(loc.file_offset, loc.data_size)); }
|
||||
|
||||
protected:
|
||||
// Read the symbols--implemented by child class.
|
||||
virtual void
|
||||
@ -171,10 +217,10 @@ class Object
|
||||
virtual void
|
||||
do_add_symbols(Symbol_table*, Read_symbols_data*) = 0;
|
||||
|
||||
// Return a view of the contents of a section. Set *PLEN to the
|
||||
// size. Implemented by child class.
|
||||
virtual const unsigned char*
|
||||
do_section_contents(unsigned int shnum, off_t* plen) = 0;
|
||||
// Return the location of the contents of a section. Implemented by
|
||||
// child class.
|
||||
virtual Location
|
||||
do_section_contents(unsigned int shnum) = 0;
|
||||
|
||||
// Get the name of a section--implemented by child class.
|
||||
virtual std::string
|
||||
@ -411,12 +457,13 @@ class Sized_relobj : public Relobj
|
||||
|
||||
// Get the name of a section.
|
||||
std::string
|
||||
do_section_name(unsigned int shnum);
|
||||
do_section_name(unsigned int shndx)
|
||||
{ return this->elf_file_.section_name(shndx); }
|
||||
|
||||
// Return a view of the contents of a section. Set *PLEN to the
|
||||
// size.
|
||||
const unsigned char*
|
||||
do_section_contents(unsigned int shnum, off_t* plen);
|
||||
// Return the location of the contents of a section.
|
||||
Location
|
||||
do_section_contents(unsigned int shndx)
|
||||
{ return this->elf_file_.section_contents(shndx); }
|
||||
|
||||
// Return the appropriate Sized_target structure.
|
||||
Sized_target<size, big_endian>*
|
||||
@ -435,10 +482,6 @@ class Sized_relobj : public Relobj
|
||||
static const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
|
||||
typedef elfcpp::Shdr<size, big_endian> Shdr;
|
||||
|
||||
// Read the section header for section SHNUM.
|
||||
const unsigned char*
|
||||
section_header(unsigned int shnum);
|
||||
|
||||
// Whether to include a section group in the link.
|
||||
bool
|
||||
include_section_group(Layout*, unsigned int,
|
||||
@ -475,16 +518,12 @@ class Sized_relobj : public Relobj
|
||||
void
|
||||
write_local_symbols(Output_file*, const Stringpool*);
|
||||
|
||||
// General access to the ELF file.
|
||||
elfcpp::Elf_file<size, big_endian, Object> elf_file_;
|
||||
// If non-NULL, a view of the section header data.
|
||||
File_view* section_headers_;
|
||||
// ELF file header e_flags field.
|
||||
unsigned int flags_;
|
||||
// File offset of section header table.
|
||||
off_t shoff_;
|
||||
// Offset of SHT_STRTAB section holding section names.
|
||||
unsigned int shstrndx_;
|
||||
// Index of SHT_SYMTAB section.
|
||||
unsigned int symtab_shnum_;
|
||||
unsigned int symtab_shndx_;
|
||||
// The number of local symbols.
|
||||
unsigned int local_symbol_count_;
|
||||
// The number of local symbols which go into the output file.
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2006-11-06 15:58-0800\n"
|
||||
"POT-Creation-Date: 2006-11-07 10:50-0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -195,113 +195,98 @@ msgstr ""
|
||||
msgid "%s: %s: TLS relocation against invalid instruction\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:41
|
||||
#, c-format
|
||||
msgid "%s: %s: bad e_ehsize field (%d != %d)\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:48
|
||||
#, c-format
|
||||
msgid "%s: %s: bad e_shentsize field (%d != %d)\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:88 object.cc:343 object.cc:447
|
||||
#, c-format
|
||||
msgid "%s: %s: bad section name offset for section %u: %lu\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:126
|
||||
#: object.cc:87
|
||||
#, c-format
|
||||
msgid "%s: %s: unsupported ELF machine number %d\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:221
|
||||
#: object.cc:173
|
||||
#, c-format
|
||||
msgid "%s: %s: invalid symbol table name index: %u\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:229
|
||||
#: object.cc:181
|
||||
#, c-format
|
||||
msgid "%s: %s: symbol table name section has wrong type: %u\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:281
|
||||
#, c-format
|
||||
msgid "%s: %s: section group %u link %u out of range\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:291
|
||||
#: object.cc:237
|
||||
#, c-format
|
||||
msgid "%s: %s: section group %u info %u out of range\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:302
|
||||
#, c-format
|
||||
msgid "%s; %s: symtab section %u link %u out of range\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:318
|
||||
#: object.cc:254
|
||||
#, c-format
|
||||
msgid "%s: %s: symbol %u name offset %u out of range\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:367
|
||||
#: object.cc:288
|
||||
#, c-format
|
||||
msgid "%s: %s: section %u in section group %u out of range"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:515
|
||||
#: object.cc:368
|
||||
#, c-format
|
||||
msgid "%s: %s: bad section name offset for section %u: %lu\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:436
|
||||
#, c-format
|
||||
msgid "%s: %s: size of symbols is not multiple of symbol size\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:601
|
||||
#: object.cc:521
|
||||
#, c-format
|
||||
msgid "%s: %s: unknown section index %u for local symbol %u\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:612
|
||||
#: object.cc:532
|
||||
#, c-format
|
||||
msgid "%s: %s: local symbol %u section index %u out of range\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:554
|
||||
#, c-format
|
||||
msgid "%s: %s: local symbol %u section name out of range: %u >= %u\n"
|
||||
msgstr ""
|
||||
|
||||
#. elfcpp::ET_DYN
|
||||
#: object.cc:800
|
||||
#: object.cc:731
|
||||
#, c-format
|
||||
msgid "%s: %s: dynamic objects are not yet supported\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:824 object.cc:877 object.cc:898
|
||||
#: object.cc:755 object.cc:808 object.cc:829
|
||||
#, c-format
|
||||
msgid "%s: %s: ELF file too short\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:833
|
||||
#: object.cc:764
|
||||
#, c-format
|
||||
msgid "%s: %s: invalid ELF version 0\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:836
|
||||
#: object.cc:767
|
||||
#, c-format
|
||||
msgid "%s: %s: unsupported ELF version %d\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:844
|
||||
#: object.cc:775
|
||||
#, c-format
|
||||
msgid "%s: %s: invalid ELF class 0\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:851
|
||||
#: object.cc:782
|
||||
#, c-format
|
||||
msgid "%s: %s: unsupported ELF class %d\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:859
|
||||
#: object.cc:790
|
||||
#, c-format
|
||||
msgid "%s: %s: invalid ELF data encoding\n"
|
||||
msgstr ""
|
||||
|
||||
#: object.cc:866
|
||||
#: object.cc:797
|
||||
#, c-format
|
||||
msgid "%s: %s: unsupported ELF data encoding %d\n"
|
||||
msgstr ""
|
||||
@ -477,22 +462,22 @@ msgstr ""
|
||||
msgid "%s: %s: unsupported symbol binding %d for symbol %s\n"
|
||||
msgstr ""
|
||||
|
||||
#: symtab.cc:440
|
||||
#: symtab.cc:441
|
||||
#, c-format
|
||||
msgid "%s: %s: mixing 32-bit and 64-bit ELF objects\n"
|
||||
msgstr ""
|
||||
|
||||
#: symtab.cc:457
|
||||
#: symtab.cc:458
|
||||
#, c-format
|
||||
msgid "%s: %s: bad global symbol name offset %u at %lu\n"
|
||||
msgstr ""
|
||||
|
||||
#: symtab.cc:882 symtab.cc:1021
|
||||
#: symtab.cc:883 symtab.cc:1022
|
||||
#, c-format
|
||||
msgid "%s: %s: unsupported symbol section 0x%x\n"
|
||||
msgstr ""
|
||||
|
||||
#: symtab.cc:1131
|
||||
#: symtab.cc:1135
|
||||
#, c-format
|
||||
msgid "%s: %s: warning: %s\n"
|
||||
msgstr ""
|
||||
|
@ -150,7 +150,7 @@ Sized_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
|
||||
|
||||
rd->relocs.reserve(shnum / 2);
|
||||
|
||||
const unsigned char *pshdrs = this->get_view(this->shoff_,
|
||||
const unsigned char *pshdrs = this->get_view(this->elf_file_.shoff(),
|
||||
shnum * This::shdr_size);
|
||||
// Skip the first, dummy, section.
|
||||
const unsigned char *ps = pshdrs + This::shdr_size;
|
||||
@ -181,7 +181,7 @@ Sized_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
|
||||
if ((secshdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0)
|
||||
continue;
|
||||
|
||||
if (shdr.get_sh_link() != this->symtab_shnum_)
|
||||
if (shdr.get_sh_link() != this->symtab_shndx_)
|
||||
{
|
||||
fprintf(stderr,
|
||||
_("%s: %s: relocation section %u uses unexpected "
|
||||
@ -227,12 +227,12 @@ Sized_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
|
||||
}
|
||||
|
||||
// Read the local symbols.
|
||||
if (this->symtab_shnum_ == 0 || this->local_symbol_count_ == 0)
|
||||
if (this->symtab_shndx_ == 0 || this->local_symbol_count_ == 0)
|
||||
rd->local_symbols = NULL;
|
||||
else
|
||||
{
|
||||
typename This::Shdr symtabshdr(pshdrs
|
||||
+ this->symtab_shnum_ * This::shdr_size);
|
||||
+ this->symtab_shndx_ * This::shdr_size);
|
||||
assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
|
||||
const int sym_size = This::sym_size;
|
||||
const unsigned int loccount = this->local_symbol_count_;
|
||||
@ -293,7 +293,7 @@ Sized_relobj<size, big_endian>::do_relocate(const General_options& options,
|
||||
unsigned int shnum = this->shnum();
|
||||
|
||||
// Read the section headers.
|
||||
const unsigned char* pshdrs = this->get_view(this->shoff_,
|
||||
const unsigned char* pshdrs = this->get_view(this->elf_file_.shoff(),
|
||||
shnum * This::shdr_size);
|
||||
|
||||
Views views;
|
||||
@ -419,7 +419,7 @@ Sized_relobj<size, big_endian>::relocate_sections(
|
||||
|
||||
assert((*pviews)[index].view != NULL);
|
||||
|
||||
if (shdr.get_sh_link() != this->symtab_shnum_)
|
||||
if (shdr.get_sh_link() != this->symtab_shndx_)
|
||||
{
|
||||
fprintf(stderr,
|
||||
_("%s: %s: relocation section %u uses unexpected "
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "object.h"
|
||||
#include "output.h"
|
||||
#include "target.h"
|
||||
#include "workqueue.h"
|
||||
#include "symtab.h"
|
||||
|
||||
namespace gold
|
||||
@ -1111,10 +1112,13 @@ Warnings::note_warnings(Symbol_table* symtab)
|
||||
// we relocate sections. That means that we can not lock
|
||||
// the object then, as we might try to issue the same
|
||||
// warning multiple times simultaneously.
|
||||
const unsigned char* c;
|
||||
off_t len;
|
||||
c = p->second.object->section_contents(p->second.shndx, &len);
|
||||
p->second.set_text(reinterpret_cast<const char*>(c), len);
|
||||
{
|
||||
Task_locker_obj<Object> tl(*p->second.object);
|
||||
const unsigned char* c;
|
||||
off_t len;
|
||||
c = p->second.object->section_contents(p->second.shndx, &len);
|
||||
p->second.set_text(reinterpret_cast<const char*>(c), len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user