Use nops when doing alignment padding between code sections.

This commit is contained in:
Ian Lance Taylor 2007-09-21 05:31:19 +00:00
parent ec51df9f10
commit c51e6221b8
5 changed files with 173 additions and 8 deletions

View File

@ -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

View File

@ -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.

View File

@ -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),

View File

@ -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.

View File

@ -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&);