Use nops when doing alignment padding between code sections.
This commit is contained in:
parent
ec51df9f10
commit
c51e6221b8
5 changed files with 173 additions and 8 deletions
68
gold/i386.cc
68
gold/i386.cc
|
@ -63,6 +63,10 @@ class Target_i386 : public Sized_target<32, false>
|
|||
elfcpp::Elf_types<32>::Elf_Addr view_address,
|
||||
off_t view_size);
|
||||
|
||||
// Return a string used to fill a code section with nops.
|
||||
std::string
|
||||
do_code_fill(off_t length);
|
||||
|
||||
private:
|
||||
// The class which scans relocations.
|
||||
struct Scan
|
||||
|
@ -212,6 +216,7 @@ const Target::Target_info Target_i386::i386_info =
|
|||
elfcpp::EM_386, // machine_code
|
||||
false, // has_make_symbol
|
||||
false, // has_resolve
|
||||
true, // has_code_fill
|
||||
"/usr/lib/libc.so.1", // dynamic_linker
|
||||
0x08048000, // text_segment_address
|
||||
0x1000, // abi_pagesize
|
||||
|
@ -1490,6 +1495,69 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
|
|||
view_size);
|
||||
}
|
||||
|
||||
// Return a string used to fill a code section with nops to take up
|
||||
// the specified length.
|
||||
|
||||
std::string
|
||||
Target_i386::do_code_fill(off_t length)
|
||||
{
|
||||
if (length >= 16)
|
||||
{
|
||||
// Build a jmp instruction to skip over the bytes.
|
||||
unsigned char jmp[5];
|
||||
jmp[0] = 0xe9;
|
||||
elfcpp::Swap_unaligned<32, false>::writeval(jmp + 1, length - 5);
|
||||
return (std::string(reinterpret_cast<char*>(&jmp[0]), 5)
|
||||
+ std::string(length - 5, '\0'));
|
||||
}
|
||||
|
||||
// Nop sequences of various lengths.
|
||||
const char nop1[1] = { 0x90 }; // nop
|
||||
const char nop2[2] = { 0x66, 0x90 }; // xchg %ax %ax
|
||||
const char nop3[3] = { 0x8d, 0x76, 0x00 }; // leal 0(%esi),%esi
|
||||
const char nop4[4] = { 0x8d, 0x74, 0x26, 0x00}; // leal 0(%esi,1),%esi
|
||||
const char nop5[5] = { 0x90, 0x8d, 0x74, 0x26, // nop
|
||||
0x00 }; // leal 0(%esi,1),%esi
|
||||
const char nop6[6] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi
|
||||
0x00, 0x00 };
|
||||
const char nop7[7] = { 0x8d, 0xb4, 0x26, 0x00, // leal 0L(%esi,1),%esi
|
||||
0x00, 0x00, 0x00 };
|
||||
const char nop8[8] = { 0x90, 0x8d, 0xb4, 0x26, // nop
|
||||
0x00, 0x00, 0x00, 0x00 }; // leal 0L(%esi,1),%esi
|
||||
const char nop9[9] = { 0x89, 0xf6, 0x8d, 0xbc, // movl %esi,%esi
|
||||
0x27, 0x00, 0x00, 0x00, // leal 0L(%edi,1),%edi
|
||||
0x00 };
|
||||
const char nop10[10] = { 0x8d, 0x76, 0x00, 0x8d, // leal 0(%esi),%esi
|
||||
0xbc, 0x27, 0x00, 0x00, // leal 0L(%edi,1),%edi
|
||||
0x00, 0x00 };
|
||||
const char nop11[11] = { 0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi
|
||||
0x8d, 0xbc, 0x27, 0x00, // leal 0L(%edi,1),%edi
|
||||
0x00, 0x00, 0x00 };
|
||||
const char nop12[12] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi
|
||||
0x00, 0x00, 0x8d, 0xbf, // leal 0L(%edi),%edi
|
||||
0x00, 0x00, 0x00, 0x00 };
|
||||
const char nop13[13] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi
|
||||
0x00, 0x00, 0x8d, 0xbc, // leal 0L(%edi,1),%edi
|
||||
0x27, 0x00, 0x00, 0x00,
|
||||
0x00 };
|
||||
const char nop14[14] = { 0x8d, 0xb4, 0x26, 0x00, // leal 0L(%esi,1),%esi
|
||||
0x00, 0x00, 0x00, 0x8d, // leal 0L(%edi,1),%edi
|
||||
0xbc, 0x27, 0x00, 0x00,
|
||||
0x00, 0x00 };
|
||||
const char nop15[15] = { 0xeb, 0x0d, 0x90, 0x90, // jmp .+15
|
||||
0x90, 0x90, 0x90, 0x90, // nop,nop,nop,...
|
||||
0x90, 0x90, 0x90, 0x90,
|
||||
0x90, 0x90, 0x90 };
|
||||
|
||||
const char* nops[16] = {
|
||||
NULL,
|
||||
nop1, nop2, nop3, nop4, nop5, nop6, nop7,
|
||||
nop8, nop9, nop10, nop11, nop12, nop13, nop14, nop15
|
||||
};
|
||||
|
||||
return std::string(nops[length], length);
|
||||
}
|
||||
|
||||
// The selector for i386 object files.
|
||||
|
||||
class Target_selector_i386 : public Target_selector
|
||||
|
|
|
@ -28,7 +28,8 @@ Layout_task_runner::run(Workqueue* workqueue)
|
|||
|
||||
// Now we know the final size of the output file and we know where
|
||||
// each piece of information goes.
|
||||
Output_file* of = new Output_file(this->options_);
|
||||
Output_file* of = new Output_file(this->options_,
|
||||
this->input_objects_->target());
|
||||
of->open(file_size);
|
||||
|
||||
// Queue up the final set of tasks.
|
||||
|
|
|
@ -863,6 +863,7 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type,
|
|||
dynsym_index_(0),
|
||||
input_sections_(),
|
||||
first_input_offset_(0),
|
||||
fills_(),
|
||||
needs_symtab_index_(false),
|
||||
needs_dynsym_index_(false),
|
||||
should_link_to_symtab_(false),
|
||||
|
@ -923,19 +924,41 @@ Output_section::add_input_section(Relobj* object, unsigned int shndx,
|
|||
}
|
||||
}
|
||||
|
||||
off_t ssize = this->data_size();
|
||||
ssize = align_address(ssize, addralign);
|
||||
this->set_data_size(ssize + shdr.get_sh_size());
|
||||
off_t offset_in_section = this->data_size();
|
||||
off_t aligned_offset_in_section = align_address(offset_in_section,
|
||||
addralign);
|
||||
|
||||
if (aligned_offset_in_section > offset_in_section
|
||||
&& (shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) != 0
|
||||
&& object->target()->has_code_fill())
|
||||
{
|
||||
// We need to add some fill data. Using fill_list_ when
|
||||
// possible is an optimization, since we will often have fill
|
||||
// sections without input sections.
|
||||
off_t fill_len = aligned_offset_in_section - offset_in_section;
|
||||
if (this->input_sections_.empty())
|
||||
this->fills_.push_back(Fill(offset_in_section, fill_len));
|
||||
else
|
||||
{
|
||||
// FIXME: When relaxing, the size needs to adjust to
|
||||
// maintain a constant alignment.
|
||||
std::string fill_data(object->target()->code_fill(fill_len));
|
||||
Output_data_const* odc = new Output_data_const(fill_data, 1);
|
||||
this->input_sections_.push_back(Input_section(odc));
|
||||
}
|
||||
}
|
||||
|
||||
this->set_data_size(aligned_offset_in_section + shdr.get_sh_size());
|
||||
|
||||
// We need to keep track of this section if we are already keeping
|
||||
// track of sections, or if we are relaxing. FIXME: Add test for
|
||||
// relaxing.
|
||||
if (! this->input_sections_.empty())
|
||||
if (!this->input_sections_.empty())
|
||||
this->input_sections_.push_back(Input_section(object, shndx,
|
||||
shdr.get_sh_size(),
|
||||
addralign));
|
||||
|
||||
return ssize;
|
||||
return aligned_offset_in_section;
|
||||
}
|
||||
|
||||
// Add arbitrary data to an output section.
|
||||
|
@ -1105,6 +1128,16 @@ Output_section::write_header(const Layout* layout,
|
|||
void
|
||||
Output_section::do_write(Output_file* of)
|
||||
{
|
||||
off_t output_section_file_offset = this->offset();
|
||||
for (Fill_list::iterator p = this->fills_.begin();
|
||||
p != this->fills_.end();
|
||||
++p)
|
||||
{
|
||||
std::string fill_data(of->target()->code_fill(p->length()));
|
||||
of->write(output_section_file_offset + p->section_offset(),
|
||||
fill_data.data(), fill_data.size());
|
||||
}
|
||||
|
||||
for (Input_section_list::iterator p = this->input_sections_.begin();
|
||||
p != this->input_sections_.end();
|
||||
++p)
|
||||
|
@ -1496,8 +1529,9 @@ Output_segment::write_section_headers_list(const Layout* layout,
|
|||
|
||||
// Output_file methods.
|
||||
|
||||
Output_file::Output_file(const General_options& options)
|
||||
Output_file::Output_file(const General_options& options, Target* target)
|
||||
: options_(options),
|
||||
target_(target),
|
||||
name_(options.output_file_name()),
|
||||
o_(-1),
|
||||
file_size_(0),
|
||||
|
|
|
@ -1533,6 +1533,37 @@ class Output_section : public Output_data
|
|||
|
||||
typedef std::vector<Input_section> Input_section_list;
|
||||
|
||||
// Fill data. This is used to fill in data between input sections.
|
||||
// When we have to keep track of the input sections, we can use an
|
||||
// Output_data_const, but we don't want to have to keep track of
|
||||
// input sections just to implement fills. For a fill we record the
|
||||
// offset, and the actual data to be written out.
|
||||
class Fill
|
||||
{
|
||||
public:
|
||||
Fill(off_t section_offset, off_t length)
|
||||
: section_offset_(section_offset), length_(length)
|
||||
{ }
|
||||
|
||||
// Return section offset.
|
||||
off_t
|
||||
section_offset() const
|
||||
{ return this->section_offset_; }
|
||||
|
||||
// Return fill length.
|
||||
off_t
|
||||
length() const
|
||||
{ return this->length_; }
|
||||
|
||||
private:
|
||||
// The offset within the output section.
|
||||
off_t section_offset_;
|
||||
// The length of the space to fill.
|
||||
off_t length_;
|
||||
};
|
||||
|
||||
typedef std::vector<Fill> Fill_list;
|
||||
|
||||
// Add a new output section by Input_section.
|
||||
void
|
||||
add_output_section_data(Input_section*);
|
||||
|
@ -1590,6 +1621,10 @@ class Output_section : public Output_data
|
|||
Input_section_list input_sections_;
|
||||
// The offset of the first entry in input_sections_.
|
||||
off_t first_input_offset_;
|
||||
// The fill data. This is separate from input_sections_ because we
|
||||
// often will need fill sections without needing to keep track of
|
||||
// input sections.
|
||||
Fill_list fills_;
|
||||
// Whether this output section needs a STT_SECTION symbol in the
|
||||
// normal symbol table. This will be true if there is a relocation
|
||||
// which needs it.
|
||||
|
@ -1765,7 +1800,12 @@ class Output_segment
|
|||
class Output_file
|
||||
{
|
||||
public:
|
||||
Output_file(const General_options& options);
|
||||
Output_file(const General_options& options, Target*);
|
||||
|
||||
// Get a pointer to the target.
|
||||
Target*
|
||||
target() const
|
||||
{ return this->target_; }
|
||||
|
||||
// Open the output file. FILE_SIZE is the final size of the file.
|
||||
void
|
||||
|
@ -1801,6 +1841,8 @@ class Output_file
|
|||
private:
|
||||
// General options.
|
||||
const General_options& options_;
|
||||
// Target.
|
||||
Target* target_;
|
||||
// File name.
|
||||
const char* name_;
|
||||
// File descriptor.
|
||||
|
|
|
@ -63,6 +63,11 @@ class Target
|
|||
has_resolve() const
|
||||
{ return this->pti_->has_resolve; }
|
||||
|
||||
// Whether this target has a specific code fill function.
|
||||
bool
|
||||
has_code_fill() const
|
||||
{ return this->pti_->has_code_fill; }
|
||||
|
||||
// Return the default name of the dynamic linker.
|
||||
const char*
|
||||
dynamic_linker() const
|
||||
|
@ -89,6 +94,13 @@ class Target
|
|||
finalize_sections(const General_options* options, Layout* layout)
|
||||
{ return this->do_finalize_sections(options, layout); }
|
||||
|
||||
// Return a string to use to fill out a code section. This is
|
||||
// basically one or more NOPS which must fill out the specified
|
||||
// length in bytes.
|
||||
std::string
|
||||
code_fill(off_t length)
|
||||
{ return this->do_code_fill(length); }
|
||||
|
||||
protected:
|
||||
// This struct holds the constant information for a child class. We
|
||||
// use a struct to avoid the overhead of virtual function calls for
|
||||
|
@ -105,6 +117,8 @@ class Target
|
|||
bool has_make_symbol;
|
||||
// Whether this target has a specific resolve function.
|
||||
bool has_resolve;
|
||||
// Whether this target has a specific code fill function.
|
||||
bool has_code_fill;
|
||||
// The default dynamic linker name.
|
||||
const char* dynamic_linker;
|
||||
// The default text segment address.
|
||||
|
@ -124,6 +138,12 @@ class Target
|
|||
do_finalize_sections(const General_options*, Layout*)
|
||||
{ }
|
||||
|
||||
// Virtual function which must be implemented by the child class if
|
||||
// needed.
|
||||
virtual std::string
|
||||
do_code_fill(off_t)
|
||||
{ gold_unreachable(); }
|
||||
|
||||
private:
|
||||
Target(const Target&);
|
||||
Target& operator=(const Target&);
|
||||
|
|
Loading…
Reference in a new issue