31 changed files with 3163 additions and 223 deletions
@ -0,0 +1,63 @@ |
|||
// i386.h -- ELF definitions specific to EM_386 -*- C++ -*-
|
|||
|
|||
#ifndef ELFCPP_I386_H |
|||
#define ELFCPP_I386_H |
|||
|
|||
namespace elfcpp |
|||
{ |
|||
|
|||
enum |
|||
{ |
|||
R_386_NONE = 0, |
|||
R_386_32 = 1, |
|||
R_386_PC32 = 2, |
|||
R_386_GOT32 = 3, |
|||
R_386_PLT32 = 4, |
|||
R_386_COPY = 5, |
|||
R_386_GLOB_DAT = 6, |
|||
R_386_JUMP_SLOT = 7, |
|||
R_386_RELATIVE = 8, |
|||
R_386_GOTOFF = 9, |
|||
R_386_GOTPC = 10, |
|||
// Used by Sun.
|
|||
R_386_32PLT = 11, |
|||
// TLS extensions.
|
|||
R_386_TLS_TPOFF = 14, |
|||
R_386_TLS_IE = 15, |
|||
R_386_TLS_GOTIE = 16, |
|||
R_386_TLS_LE = 17, |
|||
R_386_TLS_GD = 18, |
|||
R_386_TLS_LDM = 19, |
|||
// GNU extensions.
|
|||
R_386_16 = 20, |
|||
R_386_PC16 = 21, |
|||
R_386_8 = 22, |
|||
R_386_PC8 = 23, |
|||
// More TLS relocs.
|
|||
R_386_TLS_GD_32 = 24, |
|||
R_386_TLS_GD_PUSH = 25, |
|||
R_386_TLS_GD_CALL = 26, |
|||
R_386_TLS_GD_POP = 27, |
|||
R_386_TLS_LDM_32 = 28, |
|||
R_386_TLS_LDM_PUSH = 29, |
|||
R_386_TLS_LDM_CALL = 30, |
|||
R_386_TLS_LDM_POP = 31, |
|||
R_386_TLS_LDO_32 = 32, |
|||
R_386_TLS_IE_32 = 33, |
|||
R_386_TLS_LE_32 = 34, |
|||
R_386_TLS_DTPMOD32 = 35, |
|||
R_386_TLS_DTPOFF32 = 36, |
|||
R_386_TLS_TPOFF32 = 37, |
|||
R_386_TLS_GOTDESC = 39, |
|||
R_386_TLS_DESC_CALL = 40, |
|||
R_386_TLS_DESC = 41, |
|||
// Used by Intel.
|
|||
R_386_USED_BY_INTEL_200 = 200, |
|||
// GNU vtable garbage collection extensions.
|
|||
R_386_GNU_VTINHERIT = 250, |
|||
R_386_GNU_VTENTRY = 251 |
|||
}; |
|||
|
|||
} // End namespace elfcpp.
|
|||
|
|||
#endif // !defined(ELFCPP_I386_H)
|
|||
@ -0,0 +1,360 @@ |
|||
// archive.cc -- archive support for gold
|
|||
|
|||
#include "gold.h" |
|||
|
|||
#include <cerrno> |
|||
#include <cstring> |
|||
#include <climits> |
|||
#include <vector> |
|||
|
|||
#include "elfcpp.h" |
|||
#include "fileread.h" |
|||
#include "symtab.h" |
|||
#include "object.h" |
|||
#include "archive.h" |
|||
|
|||
namespace gold |
|||
{ |
|||
|
|||
// The header of an entry in the archive. This is all readable text,
|
|||
// padded with spaces where necesary. If the contents of an archive
|
|||
// are all text file, the entire archive is readable.
|
|||
|
|||
struct Archive::Archive_header |
|||
{ |
|||
// The entry name.
|
|||
char ar_name[16]; |
|||
// The file modification time.
|
|||
char ar_date[12]; |
|||
// The user's UID in decimal.
|
|||
char ar_uid[6]; |
|||
// The user's GID in decimal.
|
|||
char ar_gid[6]; |
|||
// The file mode in octal.
|
|||
char ar_mode[8]; |
|||
// The file size in decimal.
|
|||
char ar_size[10]; |
|||
// The final magic code.
|
|||
char ar_fmag[2]; |
|||
}; |
|||
|
|||
// Archive methods.
|
|||
|
|||
const char Archive::armag[sarmag] = |
|||
{ |
|||
'!', '<', 'a', 'r', 'c', 'h', '>', '\n' |
|||
}; |
|||
|
|||
const char Archive::arfmag[2] = { '`', '\n' }; |
|||
|
|||
// Get a view into the underlying file.
|
|||
|
|||
const unsigned char* |
|||
Archive::get_view(off_t start, off_t size) |
|||
{ |
|||
return this->input_file_->file().get_view(start, size); |
|||
} |
|||
|
|||
// Set up the archive: read the symbol map and the extended name
|
|||
// table.
|
|||
|
|||
void |
|||
Archive::setup() |
|||
{ |
|||
// The first member of the archive should be the symbol table.
|
|||
std::string armap_name; |
|||
off_t armap_size = this->read_header(sarmag, &armap_name); |
|||
if (!armap_name.empty()) |
|||
{ |
|||
fprintf(stderr, _("%s: %s: no archive symbol table (run ranlib)\n"), |
|||
program_name, this->name().c_str()); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
// Read in the entire armap.
|
|||
const unsigned char* p = this->get_view(sarmag + sizeof(Archive_header), |
|||
armap_size); |
|||
|
|||
// Numbers in the armap are always big-endian.
|
|||
const elfcpp::Elf_Word* pword = reinterpret_cast<const elfcpp::Elf_Word*>(p); |
|||
unsigned int nsyms = elfcpp::read_elf_word<true>(pword); |
|||
++pword; |
|||
|
|||
// Note that the addition is in units of sizeof(elfcpp::Elf_Word).
|
|||
const char* pnames = reinterpret_cast<const char*>(pword + nsyms); |
|||
|
|||
this->armap_.resize(nsyms); |
|||
|
|||
for (unsigned int i = 0; i < nsyms; ++i) |
|||
{ |
|||
this->armap_[i].name = pnames; |
|||
this->armap_[i].offset = elfcpp::read_elf_word<true>(pword); |
|||
pnames += strlen(pnames) + 1; |
|||
++pword; |
|||
} |
|||
|
|||
if (reinterpret_cast<const unsigned char*>(pnames) - p > armap_size) |
|||
{ |
|||
fprintf(stderr, _("%s: %s: bad archive symbol table names\n"), |
|||
program_name, this->name().c_str()); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
// See if there is an extended name table.
|
|||
off_t off = sarmag + sizeof(Archive_header) + armap_size; |
|||
if ((off & 1) != 0) |
|||
++off; |
|||
std::string xname; |
|||
off_t extended_size = this->read_header(off, &xname); |
|||
if (xname == "/") |
|||
{ |
|||
p = this->get_view(off + sizeof(Archive_header), extended_size); |
|||
const char* px = reinterpret_cast<const char*>(p); |
|||
this->extended_names_.assign(px, extended_size); |
|||
} |
|||
|
|||
// Opening the file locked it. Unlock it now.
|
|||
this->input_file_->file().unlock(); |
|||
} |
|||
|
|||
// Read the header of an archive member at OFF. Fail if something
|
|||
// goes wrong. Return the size of the member. Set *PNAME to the name
|
|||
// of the member.
|
|||
|
|||
off_t |
|||
Archive::read_header(off_t off, std::string* pname) |
|||
{ |
|||
const unsigned char* p = this->get_view(off, sizeof(Archive_header)); |
|||
const Archive_header* hdr = reinterpret_cast<const Archive_header*>(p); |
|||
|
|||
if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0) |
|||
{ |
|||
fprintf(stderr, _("%s; %s: malformed archive header at %ld\n"), |
|||
program_name, this->name().c_str(), |
|||
static_cast<long>(off)); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
const int size_string_size = sizeof hdr->ar_size; |
|||
char size_string[size_string_size + 1]; |
|||
memcpy(size_string, hdr->ar_size, size_string_size); |
|||
char* ps = size_string + size_string_size; |
|||
while (ps[-1] == ' ') |
|||
--ps; |
|||
*ps = '\0'; |
|||
|
|||
errno = 0; |
|||
char* end; |
|||
off_t member_size = strtol(size_string, &end, 10); |
|||
if (*end != '\0' |
|||
|| member_size < 0 |
|||
|| (member_size == LONG_MAX && errno == ERANGE)) |
|||
{ |
|||
fprintf(stderr, _("%s: %s: malformed archive header size at %ld\n"), |
|||
program_name, this->name().c_str(), |
|||
static_cast<long>(off)); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
if (hdr->ar_name[0] != '/') |
|||
{ |
|||
const char* name_end = strchr(hdr->ar_name, '/'); |
|||
if (name_end == NULL |
|||
|| name_end - hdr->ar_name >= static_cast<int>(sizeof hdr->ar_name)) |
|||
{ |
|||
fprintf(stderr, _("%s: %s: malformed archive header name at %ld\n"), |
|||
program_name, this->name().c_str(), |
|||
static_cast<long>(off)); |
|||
gold_exit(false); |
|||
} |
|||
pname->assign(hdr->ar_name, name_end - hdr->ar_name); |
|||
} |
|||
else if (hdr->ar_name[1] == ' ') |
|||
{ |
|||
// This is the symbol table.
|
|||
pname->clear(); |
|||
} |
|||
else if (hdr->ar_name[1] == '/') |
|||
{ |
|||
// This is the extended name table.
|
|||
pname->assign(1, '/'); |
|||
} |
|||
else |
|||
{ |
|||
errno = 0; |
|||
long x = strtol(hdr->ar_name + 1, &end, 10); |
|||
if (*end != ' ' |
|||
|| x < 0 |
|||
|| (x == LONG_MAX && errno == ERANGE) |
|||
|| static_cast<size_t>(x) >= this->extended_names_.size()) |
|||
{ |
|||
fprintf(stderr, _("%s: %s: bad extended name index at %ld\n"), |
|||
program_name, this->name().c_str(), |
|||
static_cast<long>(off)); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
const char* name = this->extended_names_.data() + x; |
|||
const char* name_end = strchr(name, '/'); |
|||
if (static_cast<size_t>(name_end - name) > this->extended_names_.size() |
|||
|| name_end[1] != '\n') |
|||
{ |
|||
fprintf(stderr, _("%s: %s: bad extended name entry at header %ld\n"), |
|||
program_name, this->name().c_str(), |
|||
static_cast<long>(off)); |
|||
gold_exit(false); |
|||
} |
|||
pname->assign(name, name_end - name); |
|||
} |
|||
|
|||
return member_size; |
|||
} |
|||
|
|||
// Select members from the archive and add them to the link. We walk
|
|||
// through the elements in the archive map, and look each one up in
|
|||
// the symbol table. If it exists as a strong undefined symbol, we
|
|||
// pull in the corresponding element. We have to do this in a loop,
|
|||
// since pulling in one element may create new undefined symbols which
|
|||
// may be satisfied by other objects in the archive.
|
|||
|
|||
void |
|||
Archive::add_symbols(Symbol_table* symtab, Input_objects* input_objects) |
|||
{ |
|||
size_t armap_size = this->armap_.size(); |
|||
std::vector<bool> seen; |
|||
seen.resize(this->armap_.size()); |
|||
seen.clear(); |
|||
|
|||
bool added_new_object; |
|||
do |
|||
{ |
|||
added_new_object = false; |
|||
off_t last = -1; |
|||
for (size_t i = 0; i < armap_size; ++i) |
|||
{ |
|||
if (seen[i]) |
|||
continue; |
|||
if (this->armap_[i].offset == last) |
|||
{ |
|||
seen[i] = true; |
|||
continue; |
|||
} |
|||
|
|||
Symbol* sym = symtab->lookup(this->armap_[i].name); |
|||
if (sym == NULL) |
|||
continue; |
|||
else if (sym->shnum() != elfcpp::SHN_UNDEF) |
|||
{ |
|||
seen[i] = true; |
|||
continue; |
|||
} |
|||
else if (sym->binding() == elfcpp::STB_WEAK) |
|||
continue; |
|||
|
|||
// We want to include this object in the link.
|
|||
last = this->armap_[i].offset; |
|||
this->include_member(symtab, input_objects, last); |
|||
added_new_object = true; |
|||
} |
|||
} |
|||
while (added_new_object); |
|||
} |
|||
|
|||
// Include an archive member in the link. OFF is the file offset of
|
|||
// the member header.
|
|||
|
|||
void |
|||
Archive::include_member(Symbol_table* symtab, Input_objects* input_objects, |
|||
off_t off) |
|||
{ |
|||
std::string n; |
|||
this->read_header(off, &n); |
|||
|
|||
size_t memoff = off + sizeof(Archive_header); |
|||
|
|||
// Read enough of the file to pick up the entire ELF header.
|
|||
int ehdr_size = elfcpp::Elf_sizes<64>::ehdr_size; |
|||
off_t bytes; |
|||
const unsigned char* p = this->input_file_->file().get_view(memoff, |
|||
ehdr_size, |
|||
&bytes); |
|||
if (bytes < 4) |
|||
{ |
|||
fprintf(stderr, _("%s: %s: member at %ld is not an ELF object"), |
|||
program_name, this->name().c_str(), |
|||
static_cast<long>(off)); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
static unsigned char elfmagic[4] = |
|||
{ |
|||
elfcpp::ELFMAG0, elfcpp::ELFMAG1, |
|||
elfcpp::ELFMAG2, elfcpp::ELFMAG3 |
|||
}; |
|||
if (memcmp(p, elfmagic, 4) != 0) |
|||
{ |
|||
fprintf(stderr, _("%s: %s: member at %ld is not an ELF object"), |
|||
program_name, this->name().c_str(), |
|||
static_cast<long>(off)); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
Object* obj = make_elf_object((std::string(this->input_file_->name()) |
|||
+ "(" + n + ")"), |
|||
this->input_file_, memoff, p, bytes); |
|||
|
|||
input_objects->add_object(obj); |
|||
|
|||
Read_symbols_data sd = obj->read_symbols(); |
|||
obj->add_symbols(symtab, sd); |
|||
} |
|||
|
|||
// Add_archive_symbols methods.
|
|||
|
|||
Add_archive_symbols::~Add_archive_symbols() |
|||
{ |
|||
if (this->this_blocker_ != NULL) |
|||
delete this->this_blocker_; |
|||
// next_blocker_ is deleted by the task associated with the next
|
|||
// input file.
|
|||
} |
|||
|
|||
// Return whether we can add the archive symbols. We are blocked by
|
|||
// this_blocker_. We block next_blocker_. We also lock the file.
|
|||
|
|||
Task::Is_runnable_type |
|||
Add_archive_symbols::is_runnable(Workqueue*) |
|||
{ |
|||
if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) |
|||
return IS_BLOCKED; |
|||
return IS_RUNNABLE; |
|||
} |
|||
|
|||
class Add_archive_symbols::Add_archive_symbols_locker : public Task_locker |
|||
{ |
|||
public: |
|||
Add_archive_symbols_locker(Task_token& token, Workqueue* workqueue, |
|||
Archive* archive) |
|||
: blocker_(token, workqueue), archlock_(*archive) |
|||
{ } |
|||
|
|||
private: |
|||
Task_locker_block blocker_; |
|||
Task_locker_obj<Archive> archlock_; |
|||
}; |
|||
|
|||
Task_locker* |
|||
Add_archive_symbols::locks(Workqueue* workqueue) |
|||
{ |
|||
return new Add_archive_symbols_locker(*this->next_blocker_, |
|||
workqueue, |
|||
this->archive_); |
|||
} |
|||
|
|||
void |
|||
Add_archive_symbols::run(Workqueue*) |
|||
{ |
|||
this->archive_->add_symbols(this->symtab_, this->input_objects_); |
|||
} |
|||
|
|||
} // End namespace gold.
|
|||
@ -0,0 +1,143 @@ |
|||
// archive.h -- archive support for gold -*- C++ -*-
|
|||
|
|||
#ifndef GOLD_ARCHIVE_H |
|||
#define GOLD_ARCHIVE_H |
|||
|
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
#include "workqueue.h" |
|||
|
|||
namespace gold |
|||
{ |
|||
|
|||
class Input_file; |
|||
class Input_objects; |
|||
class Symbol_table; |
|||
|
|||
// This class represents an archive--generally a libNAME.a file.
|
|||
// Archives have a symbol table and a list of objects.
|
|||
|
|||
class Archive |
|||
{ |
|||
public: |
|||
Archive(const std::string& name, Input_file* input_file) |
|||
: name_(name), input_file_(input_file), armap_(), extended_names_() |
|||
{ } |
|||
|
|||
// The length of the magic string at the start of an archive.
|
|||
static const int sarmag = 8; |
|||
|
|||
// The magic string at the start of an archive.
|
|||
static const char armag[sarmag]; |
|||
|
|||
// The string expected at the end of an archive member header.
|
|||
static const char arfmag[2]; |
|||
|
|||
// The name of the object.
|
|||
const std::string& |
|||
name() const |
|||
{ return this->name_; } |
|||
|
|||
// Set up the archive: read the symbol map.
|
|||
void |
|||
setup(); |
|||
|
|||
// Lock the underlying file.
|
|||
void |
|||
lock() |
|||
{ this->input_file_->file().lock(); } |
|||
|
|||
// Unlock the underlying file.
|
|||
void |
|||
unlock() |
|||
{ this->input_file_->file().unlock(); } |
|||
|
|||
// Return whether the underlying file is locked.
|
|||
bool |
|||
is_locked() const |
|||
{ return this->input_file_->file().is_locked(); } |
|||
|
|||
// Select members from the archive as needed and add them to the
|
|||
// link.
|
|||
void |
|||
add_symbols(Symbol_table*, Input_objects*); |
|||
|
|||
private: |
|||
Archive(const Archive&); |
|||
Archive& operator=(const Archive&); |
|||
|
|||
struct Archive_header; |
|||
class Add_archive_symbols_locker; |
|||
|
|||
// Get a view into the underlying file.
|
|||
const unsigned char* |
|||
get_view(off_t start, off_t size); |
|||
|
|||
// Read an archive member header at OFF. Return the size of the
|
|||
// member, and set *PNAME to the name.
|
|||
off_t |
|||
read_header(off_t off, std::string* pname); |
|||
|
|||
// Include an archive member in the link.
|
|||
void |
|||
include_member(Symbol_table*, Input_objects*, off_t off); |
|||
|
|||
// An entry in the archive map of symbols to object files.
|
|||
struct Armap_entry |
|||
{ |
|||
// The symbol name.
|
|||
const char* name; |
|||
// The offset to the file.
|
|||
off_t offset; |
|||
}; |
|||
|
|||
// Name of object as printed to user.
|
|||
std::string name_; |
|||
// For reading the file.
|
|||
Input_file* input_file_; |
|||
// The archive map.
|
|||
std::vector<Armap_entry> armap_; |
|||
// The extended name table.
|
|||
std::string extended_names_; |
|||
}; |
|||
|
|||
// This class is used to read an archive and pick out the desired
|
|||
// elements and add them to the link.
|
|||
|
|||
class Add_archive_symbols : public Task |
|||
{ |
|||
public: |
|||
Add_archive_symbols(Symbol_table* symtab, Input_objects* input_objects, |
|||
Archive* archive, Task_token* this_blocker, |
|||
Task_token* next_blocker) |
|||
: symtab_(symtab), input_objects_(input_objects), archive_(archive), |
|||
this_blocker_(this_blocker), next_blocker_(next_blocker) |
|||
{ } |
|||
|
|||
~Add_archive_symbols(); |
|||
|
|||
// The standard Task methods.
|
|||
|
|||
Is_runnable_type |
|||
is_runnable(Workqueue*); |
|||
|
|||
Task_locker* |
|||
locks(Workqueue*); |
|||
|
|||
void |
|||
run(Workqueue*); |
|||
|
|||
private: |
|||
class Add_archive_symbols_locker; |
|||
|
|||
Symbol_table* symtab_; |
|||
Input_objects* input_objects_; |
|||
Archive* archive_; |
|||
Task_token* this_blocker_; |
|||
Task_token* next_blocker_; |
|||
}; |
|||
|
|||
} // End namespace gold.
|
|||
|
|||
#endif // !defined(GOLD_ARCHIVE_H)
|
|||
@ -0,0 +1,260 @@ |
|||
// reloc.cc -- relocate input files for gold.
|
|||
|
|||
#include "gold.h" |
|||
|
|||
#include "workqueue.h" |
|||
#include "object.h" |
|||
#include "output.h" |
|||
#include "reloc.h" |
|||
|
|||
namespace gold |
|||
{ |
|||
|
|||
// Relocate_task methods.
|
|||
|
|||
// These tasks are always runnable.
|
|||
|
|||
Task::Is_runnable_type |
|||
Relocate_task::is_runnable(Workqueue*) |
|||
{ |
|||
return IS_RUNNABLE; |
|||
} |
|||
|
|||
// We want to lock the file while we run. We want to unblock
|
|||
// FINAL_BLOCKER when we are done.
|
|||
|
|||
class Relocate_task::Relocate_locker : public Task_locker |
|||
{ |
|||
public: |
|||
Relocate_locker(Task_token& token, Workqueue* workqueue, |
|||
Object* object) |
|||
: blocker_(token, workqueue), objlock_(*object) |
|||
{ } |
|||
|
|||
private: |
|||
Task_locker_block blocker_; |
|||
Task_locker_obj<Object> objlock_; |
|||
}; |
|||
|
|||
Task_locker* |
|||
Relocate_task::locks(Workqueue* workqueue) |
|||
{ |
|||
return new Relocate_locker(*this->final_blocker_, workqueue, |
|||
this->object_); |
|||
} |
|||
|
|||
// Run the task.
|
|||
|
|||
void |
|||
Relocate_task::run(Workqueue*) |
|||
{ |
|||
this->object_->relocate(this->options_, this->symtab_, this->sympool_, |
|||
this->of_); |
|||
} |
|||
|
|||
// Relocate the input sections and write out the local symbols.
|
|||
|
|||
template<int size, bool big_endian> |
|||
void |
|||
Sized_object<size, big_endian>::do_relocate(const General_options&, |
|||
const Symbol_table* symtab, |
|||
const Stringpool* sympool, |
|||
Output_file* of) |
|||
{ |
|||
unsigned int shnum = this->shnum(); |
|||
|
|||
// Read the section headers.
|
|||
const unsigned char* pshdrs = this->get_view(this->shoff_, |
|||
shnum * This::shdr_size); |
|||
|
|||
Views views; |
|||
views.resize(shnum); |
|||
|
|||
// Make two passes over the sections. The first one copies the
|
|||
// section data to the output file. The second one applies
|
|||
// relocations.
|
|||
|
|||
this->write_sections(pshdrs, of, &views); |
|||
|
|||
// Apply relocations.
|
|||
|
|||
this->relocate_sections(symtab, pshdrs, &views); |
|||
|
|||
// Write out the accumulated views.
|
|||
for (unsigned int i = 1; i < shnum; ++i) |
|||
{ |
|||
if (views[i].view != NULL) |
|||
of->write_output_view(views[i].offset, views[i].view_size, |
|||
views[i].view); |
|||
} |
|||
|
|||
// Write out the local symbols.
|
|||
this->write_local_symbols(of, sympool); |
|||
} |
|||
|
|||
// Write section data to the output file. PSHDRS points to the
|
|||
// section headers. Record the views in *PVIEWS for use when
|
|||
// relocating.
|
|||
|
|||
template<int size, bool big_endian> |
|||
void |
|||
Sized_object<size, big_endian>::write_sections(const unsigned char* pshdrs, |
|||
Output_file* of, |
|||
Views* pviews) |
|||
{ |
|||
unsigned int shnum = this->shnum(); |
|||
std::vector<Map_to_output>& map_sections(this->map_to_output()); |
|||
|
|||
const unsigned char* p = pshdrs + This::shdr_size; |
|||
for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) |
|||
{ |
|||
View_size* pvs = &(*pviews)[i]; |
|||
|
|||
pvs->view = NULL; |
|||
|
|||
const Output_section* os = map_sections[i].output_section; |
|||
if (os == NULL) |
|||
continue; |
|||
|
|||
typename This::Shdr shdr(p); |
|||
|
|||
if (shdr.get_sh_type() == elfcpp::SHT_NOBITS) |
|||
continue; |
|||
|
|||
assert(map_sections[i].offset >= 0 |
|||
&& map_sections[i].offset < os->data_size()); |
|||
off_t start = os->offset() + map_sections[i].offset; |
|||
off_t sh_size = shdr.get_sh_size(); |
|||
|
|||
unsigned char* view = of->get_output_view(start, sh_size); |
|||
this->input_file()->file().read(shdr.get_sh_offset(), |
|||
sh_size, |
|||
view); |
|||
pvs->view = view; |
|||
pvs->address = os->address() + map_sections[i].offset; |
|||
pvs->offset = start; |
|||
pvs->view_size = sh_size; |
|||
} |
|||
} |
|||
|
|||
// Relocate section data. VIEWS points to the section data as views
|
|||
// in the output file.
|
|||
|
|||
template<int size, bool big_endian> |
|||
void |
|||
Sized_object<size, big_endian>::relocate_sections(const Symbol_table* symtab, |
|||
const unsigned char* pshdrs, |
|||
Views* pviews) |
|||
{ |
|||
unsigned int shnum = this->shnum(); |
|||
std::vector<Map_to_output>& map_sections(this->map_to_output()); |
|||
Sized_target<size, big_endian>* target = this->sized_target(); |
|||
|
|||
const unsigned char* p = pshdrs + This::shdr_size; |
|||
for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) |
|||
{ |
|||
typename This::Shdr shdr(p); |
|||
|
|||
unsigned int sh_type = shdr.get_sh_type(); |
|||
if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA) |
|||
continue; |
|||
|
|||
unsigned int index = shdr.get_sh_info(); |
|||
if (index >= this->shnum()) |
|||
{ |
|||
fprintf(stderr, _("%s: %s: relocation section %u has bad info %u\n"), |
|||
program_name, this->name().c_str(), i, index); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
if (map_sections[index].output_section == NULL) |
|||
{ |
|||
// This relocation section is against a section which we
|
|||
// discarded.
|
|||
continue; |
|||
} |
|||
|
|||
assert((*pviews)[index].view != NULL); |
|||
|
|||
if (shdr.get_sh_link() != this->symtab_shnum_) |
|||
{ |
|||
fprintf(stderr, |
|||
_("%s: %s: relocation section %u uses unexpected " |
|||
"symbol table %u\n"), |
|||
program_name, this->name().c_str(), i, shdr.get_sh_link()); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
off_t sh_size = shdr.get_sh_size(); |
|||
const unsigned char* prelocs = this->get_view(shdr.get_sh_offset(), |
|||
sh_size); |
|||
|
|||
unsigned int reloc_size; |
|||
if (sh_type == elfcpp::SHT_REL) |
|||
reloc_size = elfcpp::Elf_sizes<size>::rel_size; |
|||
else |
|||
reloc_size = elfcpp::Elf_sizes<size>::rela_size; |
|||
|
|||
if (reloc_size != shdr.get_sh_entsize()) |
|||
{ |
|||
fprintf(stderr, |
|||
_("%s: %s: unexpected entsize for reloc section %u: " |
|||
"%lu != %u"), |
|||
program_name, this->name().c_str(), i, |
|||
static_cast<unsigned long>(shdr.get_sh_entsize()), |
|||
reloc_size); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
size_t reloc_count = sh_size / reloc_size; |
|||
if (reloc_count * reloc_size != sh_size) |
|||
{ |
|||
fprintf(stderr, _("%s: %s: reloc section %u size %lu uneven"), |
|||
program_name, this->name().c_str(), i, |
|||
static_cast<unsigned long>(sh_size)); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
target->relocate_section(symtab, this, sh_type, prelocs, reloc_count, |
|||
this->local_symbol_count_, |
|||
this->values_, |
|||
this->symbols_, |
|||
(*pviews)[index].view, |
|||
(*pviews)[index].address, |
|||
(*pviews)[index].view_size); |
|||
} |
|||
} |
|||
|
|||
// Instantiate the templates we need. We could use the configure
|
|||
// script to restrict this to only the ones for implemented targets.
|
|||
|
|||
template |
|||
void |
|||
Sized_object<32, false>::do_relocate(const General_options& options, |
|||
const Symbol_table* symtab, |
|||
const Stringpool* sympool, |
|||
Output_file* of); |
|||
|
|||
template |
|||
void |
|||
Sized_object<32, true>::do_relocate(const General_options& options, |
|||
const Symbol_table* symtab, |
|||
const Stringpool* sympool, |
|||
Output_file* of); |
|||
|
|||
template |
|||
void |
|||
Sized_object<64, false>::do_relocate(const General_options& options, |
|||
const Symbol_table* symtab, |
|||
const Stringpool* sympool, |
|||
Output_file* of); |
|||
|
|||
template |
|||
void |
|||
Sized_object<64, true>::do_relocate(const General_options& options, |
|||
const Symbol_table* symtab, |
|||
const Stringpool* sympool, |
|||
Output_file* of); |
|||
|
|||
|
|||
} // End namespace gold.
|
|||
@ -0,0 +1,45 @@ |
|||
// reloc.h -- relocate input files for gold -*- C++ -*-
|
|||
|
|||
#ifndef GOLD_RELOC_H |
|||
#define GOLD_RELOC_H |
|||
|
|||
#include "workqueue.h" |
|||
|
|||
namespace gold |
|||
{ |
|||
|
|||
class Relocate_task : public Task |
|||
{ |
|||
public: |
|||
Relocate_task(const General_options& options, const Symbol_table* symtab, |
|||
const Stringpool* sympool, Object* object, Output_file* of, |
|||
Task_token* final_blocker) |
|||
: options_(options), symtab_(symtab), sympool_(sympool), object_(object), |
|||
of_(of), final_blocker_(final_blocker) |
|||
{ } |
|||
|
|||
// The standard Task methods.
|
|||
|
|||
Is_runnable_type |
|||
is_runnable(Workqueue*); |
|||
|
|||
Task_locker* |
|||
locks(Workqueue*); |
|||
|
|||
void |
|||
run(Workqueue*); |
|||
|
|||
private: |
|||
class Relocate_locker; |
|||
|
|||
const General_options& options_; |
|||
const Symbol_table* symtab_; |
|||
const Stringpool* sympool_; |
|||
Object* object_; |
|||
Output_file* of_; |
|||
Task_token* final_blocker_; |
|||
}; |
|||
|
|||
} // End namespace gold.
|
|||
|
|||
#endif // !defined(GOLD_RELOC_H)
|
|||
@ -0,0 +1,119 @@ |
|||
// target-reloc.h -- target specific relocation support -*- C++ -*-
|
|||
|
|||
#ifndef GOLD_TARGET_RELOC_H |
|||
#define GOLD_TARGET_RELOC_H |
|||
|
|||
#include "elfcpp.h" |
|||
#include "symtab.h" |
|||
|
|||
namespace gold |
|||
{ |
|||
|
|||
// Pick the ELF relocation accessor class and the size based on
|
|||
// SH_TYPE, which is either SHT_REL or SHT_RELA.
|
|||
|
|||
template<int sh_type, int size, bool big_endian> |
|||
struct Reloc_types; |
|||
|
|||
template<int size, bool big_endian> |
|||
struct Reloc_types<elfcpp::SHT_REL, size, big_endian> |
|||
{ |
|||
typedef typename elfcpp::Rel<size, big_endian> Reloc; |
|||
static const int reloc_size = elfcpp::Elf_sizes<size>::rel_size; |
|||
}; |
|||
|
|||
template<int size, bool big_endian> |
|||
struct Reloc_types<elfcpp::SHT_RELA, size, big_endian> |
|||
{ |
|||
typedef typename elfcpp::Rela<size, big_endian> Reloc; |
|||
static const int reloc_size = elfcpp::Elf_sizes<size>::rela_size; |
|||
}; |
|||
|
|||
// This function implements the generic part of relocation handling.
|
|||
// This is an inline function which take a class whose operator()
|
|||
// implements the machine specific part of relocation. We do it this
|
|||
// way to avoid making a function call for each relocation, and to
|
|||
// avoid repeating the generic relocation handling code for each
|
|||
// target.
|
|||
|
|||
// SIZE is the ELF size: 32 or 64. BIG_ENDIAN is the endianness of
|
|||
// the data. SH_TYPE is the section type: SHT_REL or SHT_RELA. RELOC
|
|||
// implements operator() to do a relocation.
|
|||
|
|||
// OBJECT is the object for we are processing relocs. SH_TYPE is the
|
|||
// type of relocation: SHT_REL or SHT_RELA. PRELOCS points to the
|
|||
// relocation data. RELOC_COUNT is the number of relocs. LOCAL_COUNT
|
|||
// is the number of local symbols. LOCAL_VALUES holds the values of
|
|||
// the local symbols. GLOBAL_SYMS points to the global symbols. VIEW
|
|||
// is the section data, VIEW_ADDRESS is its memory address, and
|
|||
// VIEW_SIZE is the size.
|
|||
|
|||
template<int size, bool big_endian, int sh_type, typename Relocate> |
|||
inline void |
|||
relocate_section( |
|||
const Symbol_table* symtab, |
|||
Sized_object<size, big_endian>* object, |
|||
const unsigned char* prelocs, |
|||
size_t reloc_count, |
|||
size_t local_count, |
|||
const typename elfcpp::Elf_types<size>::Elf_Addr* local_values, |
|||
Symbol** global_syms, |
|||
unsigned char* view, |
|||
typename elfcpp::Elf_types<size>::Elf_Addr view_address, |
|||
off_t view_size) |
|||
{ |
|||
typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype; |
|||
const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size; |
|||
Relocate relocate; |
|||
|
|||
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) |
|||
{ |
|||
Reltype reloc(prelocs); |
|||
|
|||
off_t offset = reloc.get_r_offset(); |
|||
if (offset < 0 || offset >= view_size) |
|||
{ |
|||
fprintf(stderr, _("%s: %s: reloc %zu has bad offset %lu\n"), |
|||
program_name, object->name().c_str(), i, |
|||
static_cast<unsigned long>(offset)); |
|||
gold_exit(false); |
|||
} |
|||
|
|||
typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info(); |
|||
unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info); |
|||
unsigned int r_type = elfcpp::elf_r_type<size>(r_info); |
|||
|
|||
Sized_symbol<size>* sym; |
|||
typename elfcpp::Elf_types<size>::Elf_Addr value; |
|||
|
|||
if (r_sym < local_count) |
|||
{ |
|||
sym = NULL; |
|||
value = local_values[r_sym]; |
|||
} |
|||
else |
|||
{ |
|||
Symbol* gsym = global_syms[r_sym - local_count]; |
|||
if (gsym->is_forwarder()) |
|||
gsym = symtab->resolve_forwards(gsym); |
|||
|
|||
sym = static_cast<Sized_symbol<size>*>(gsym); |
|||
value = sym->value(); |
|||
|
|||
if (sym->shnum() == elfcpp::SHN_UNDEF |
|||
&& sym->binding() != elfcpp::STB_WEAK) |
|||
{ |
|||
fprintf(stderr, _("%s: %s: undefined reference to '%s'\n"), |
|||
program_name, object->name().c_str(), sym->name()); |
|||
// gold_exit(false);
|
|||
} |
|||
} |
|||
|
|||
relocate(object, reloc, r_type, sym, value, view + offset, |
|||
view_address + offset); |
|||
} |
|||
} |
|||
|
|||
} // End namespace gold.
|
|||
|
|||
#endif // !defined(GOLD_TARGET_RELOC_H)
|
|||
Loading…
Reference in new issue