Support --oformat binary.

This commit is contained in:
Ian Lance Taylor 2008-02-07 07:33:46 +00:00
parent 1ef1f3d387
commit 516cb3d01e
7 changed files with 154 additions and 20 deletions

View File

@ -195,6 +195,10 @@ queue_middle_tasks(const General_options& options,
if (!doing_static_link && parameters->output_is_object())
gold_error(_("cannot mix -r with dynamic object %s"),
(*input_objects->dynobj_begin())->name().c_str());
if (!doing_static_link
&& options.output_format() != General_options::OUTPUT_FORMAT_ELF)
gold_fatal(_("cannot use non-ELF output format with dynamic object %s"),
(*input_objects->dynobj_begin())->name().c_str());
if (is_debugging_enabled(DEBUG_SCRIPT))
layout->script_options()->print(stderr);
@ -365,7 +369,8 @@ queue_final_tasks(const General_options& options,
// Queue a task to close the output file. This will be blocked by
// FINAL_BLOCKER.
workqueue->queue(new Task_function(new Close_task_runner(of),
workqueue->queue(new Task_function(new Close_task_runner(&options, layout,
of),
final_blocker,
"Task_function Close_task_runner"));
}

View File

@ -57,6 +57,8 @@ Layout_task_runner::run(Workqueue* workqueue, const Task* task)
// 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(parameters->output_file_name());
if (this->options_.output_format() != General_options::OUTPUT_FORMAT_ELF)
of->set_is_temporary();
of->open(file_size);
// Queue up the final set of tasks.
@ -949,6 +951,9 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
else
load_seg = this->find_first_load_seg();
if (this->options_.output_format() != General_options::OUTPUT_FORMAT_ELF)
load_seg = NULL;
gold_assert(phdr_seg == NULL || load_seg != NULL);
// Lay out the segment headers.
@ -2486,6 +2491,55 @@ Layout::write_sections_after_input_sections(Output_file* of)
this->section_headers_->write(of);
}
// Write out a binary file. This is called after the link is
// complete. IN is the temporary output file we used to generate the
// ELF code. We simply walk through the segments, read them from
// their file offset in IN, and write them to their load address in
// the output file. FIXME: with a bit more work, we could support
// S-records and/or Intel hex format here.
void
Layout::write_binary(Output_file* in) const
{
gold_assert(this->options_.output_format()
== General_options::OUTPUT_FORMAT_BINARY);
// Get the size of the binary file.
uint64_t max_load_address = 0;
for (Segment_list::const_iterator p = this->segment_list_.begin();
p != this->segment_list_.end();
++p)
{
if ((*p)->type() == elfcpp::PT_LOAD && (*p)->filesz() > 0)
{
uint64_t max_paddr = (*p)->paddr() + (*p)->filesz();
if (max_paddr > max_load_address)
max_load_address = max_paddr;
}
}
Output_file out(parameters->output_file_name());
out.open(max_load_address);
for (Segment_list::const_iterator p = this->segment_list_.begin();
p != this->segment_list_.end();
++p)
{
if ((*p)->type() == elfcpp::PT_LOAD && (*p)->filesz() > 0)
{
const unsigned char* vin = in->get_input_view((*p)->offset(),
(*p)->filesz());
unsigned char* vout = out.get_output_view((*p)->paddr(),
(*p)->filesz());
memcpy(vout, vin, (*p)->filesz());
out.write_output_view((*p)->paddr(), (*p)->filesz(), vout);
in->free_input_view((*p)->offset(), (*p)->filesz(), vin);
}
}
out.close();
}
// Print statistical information to stderr. This is used for --stats.
void
@ -2617,6 +2671,10 @@ Write_after_input_sections_task::run(Workqueue*)
void
Close_task_runner::run(Workqueue*, const Task*)
{
// If we've been asked to create a binary file, we do so here.
if (this->options_->output_format() != General_options::OUTPUT_FORMAT_ELF)
this->layout_->write_binary(this->of_);
this->of_->close();
}

View File

@ -287,6 +287,10 @@ class Layout
script_options() const
{ return this->script_options_; }
// Rewrite output file in binary format.
void
write_binary(Output_file* in) const;
// Dump statistical information to stderr.
void
print_stats() const;
@ -732,8 +736,9 @@ class Write_after_input_sections_task : public Task
class Close_task_runner : public Task_function_runner
{
public:
Close_task_runner(Output_file* of)
: of_(of)
Close_task_runner(const General_options* options, const Layout* layout,
Output_file* of)
: options_(options), layout_(layout), of_(of)
{ }
// Run the operation.
@ -741,6 +746,8 @@ class Close_task_runner : public Task_function_runner
run(Workqueue*, const Task*);
private:
const General_options* options_;
const Layout* layout_;
Output_file* of_;
};

View File

@ -477,6 +477,9 @@ options::Command_line_options::options[] =
GENERAL_ARG('O', NULL, N_("Optimize output file size"),
N_("-O level"), ONE_DASH,
&General_options::set_optimization_level),
GENERAL_ARG('\0', "oformat", N_("Set output format (only binary supported)"),
N_("--oformat FORMAT"), EXACTLY_TWO_DASHES,
&General_options::set_output_format),
GENERAL_NOARG('r', NULL, N_("Generate relocatable output"), NULL,
ONE_DASH, &General_options::set_relocatable),
// -R really means -rpath, but can mean --just-symbols for
@ -605,6 +608,7 @@ General_options::General_options(Script_options* script_options)
search_path_(),
optimization_level_(0),
output_file_name_("a.out"),
output_format_(OUTPUT_FORMAT_ELF),
is_relocatable_(false),
strip_(STRIP_NONE),
allow_shlib_undefined_(false),
@ -643,6 +647,24 @@ General_options::define_symbol(const char* arg)
this->script_options_->define_symbol(arg);
}
// Handle the --oformat option. The GNU linker accepts a target name
// with --oformat. In practice for an ELF target this would be the
// same target as the input files. That name always start with "elf".
// Non-ELF targets would be "srec", "symbolsrec", "tekhex", "binary",
// "ihex".
void
General_options::set_output_format(const char* arg)
{
if (strncmp(arg, "elf", 3) == 0)
this->output_format_ = OUTPUT_FORMAT_ELF;
else if (strcmp(arg, "binary") == 0)
this->output_format_ = OUTPUT_FORMAT_BINARY;
else
gold_error(_("format '%s' not supported (supported formats: elf, binary)"),
arg);
}
// Handle the -z option.
void
@ -855,8 +877,8 @@ Command_line::process_one_option(int argc, char** argv, int i,
{
if (options[j].long_option != NULL
&& (dashes == 2
|| (options[j].dash
!= options::One_option::EXACTLY_TWO_DASHES))
|| (options[j].dash
!= options::One_option::EXACTLY_TWO_DASHES))
&& first == options[j].long_option[0]
&& strcmp(opt, options[j].long_option) == 0)
{
@ -1023,6 +1045,10 @@ Command_line::normalize_options()
if (this->options_.is_shared() && this->options_.is_relocatable())
gold_fatal(_("-shared and -r are incompatible"));
if (this->options_.output_format() != General_options::OUTPUT_FORMAT_ELF
&& (this->options_.is_shared() || this->options_.is_relocatable()))
gold_fatal(_("binary output format not compatible with -shared or -r"));
// If the user specifies both -s and -r, convert the -s as -S.
// -r requires us to keep externally visible symbols!
if (this->options_.strip_all() && this->options_.is_relocatable())

View File

@ -148,6 +148,20 @@ class General_options
output_file_name() const
{ return this->output_file_name_; }
// --oformat: Output format.
enum Output_format
{
// Ordinary ELF.
OUTPUT_FORMAT_ELF,
// Straight binary format.
OUTPUT_FORMAT_BINARY
};
Output_format
output_format() const
{ return this->output_format_; }
// -r: Whether we are doing a relocatable link.
bool
is_relocatable() const
@ -370,6 +384,9 @@ class General_options
set_output_file_name(const char* arg)
{ this->output_file_name_ = arg; }
void
set_output_format(const char*);
void
set_relocatable()
{ this->is_relocatable_ = true; }
@ -544,6 +561,7 @@ class General_options
Dir_list search_path_;
int optimization_level_;
const char* output_file_name_;
Output_format output_format_;
bool is_relocatable_;
Strip strip_;
bool allow_shlib_undefined_;

View File

@ -2755,7 +2755,8 @@ Output_file::Output_file(const char* name)
o_(-1),
file_size_(0),
base_(NULL),
map_is_anonymous_(false)
map_is_anonymous_(false),
is_temporary_(false)
{
}
@ -2780,19 +2781,22 @@ Output_file::open(off_t file_size)
// to improve the odds for open().
// We let the name "-" mean "stdout"
if (strcmp(this->name_, "-") == 0)
this->o_ = STDOUT_FILENO;
else
if (!this->is_temporary_)
{
struct stat s;
if (::stat(this->name_, &s) == 0 && s.st_size != 0)
unlink_if_ordinary(this->name_);
if (strcmp(this->name_, "-") == 0)
this->o_ = STDOUT_FILENO;
else
{
struct stat s;
if (::stat(this->name_, &s) == 0 && s.st_size != 0)
unlink_if_ordinary(this->name_);
int mode = parameters->output_is_object() ? 0666 : 0777;
int o = ::open(this->name_, O_RDWR | O_CREAT | O_TRUNC, mode);
if (o < 0)
gold_fatal(_("%s: open: %s"), this->name_, strerror(errno));
this->o_ = o;
int mode = parameters->output_is_object() ? 0666 : 0777;
int o = ::open(this->name_, O_RDWR | O_CREAT | O_TRUNC, mode);
if (o < 0)
gold_fatal(_("%s: open: %s"), this->name_, strerror(errno));
this->o_ = o;
}
}
this->map();
@ -2837,7 +2841,8 @@ Output_file::map()
struct stat statbuf;
if (o == STDOUT_FILENO || o == STDERR_FILENO
|| ::fstat(o, &statbuf) != 0
|| !S_ISREG(statbuf.st_mode))
|| !S_ISREG(statbuf.st_mode)
|| this->is_temporary_)
{
this->map_is_anonymous_ = true;
base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
@ -2878,7 +2883,7 @@ void
Output_file::close()
{
// If the map isn't file-backed, we need to write it now.
if (this->map_is_anonymous_)
if (this->map_is_anonymous_ && !this->is_temporary_)
{
size_t bytes_to_write = this->file_size_;
while (bytes_to_write > 0)
@ -2895,7 +2900,9 @@ Output_file::close()
this->unmap();
// We don't close stdout or stderr
if (this->o_ != STDOUT_FILENO && this->o_ != STDERR_FILENO)
if (this->o_ != STDOUT_FILENO
&& this->o_ != STDERR_FILENO
&& !this->is_temporary_)
if (::close(this->o_) < 0)
gold_error(_("%s: close: %s"), this->name_, strerror(errno));
this->o_ = -1;

View File

@ -2393,6 +2393,11 @@ class Output_segment
filesz() const
{ return this->filesz_; }
// Return the file offset.
off_t
offset() const
{ return this->offset_; }
// Return the maximum alignment of the Output_data.
uint64_t
maximum_alignment();
@ -2571,6 +2576,12 @@ class Output_file
public:
Output_file(const char* name);
// Indicate that this is a temporary file which should not be
// output.
void
set_is_temporary()
{ this->is_temporary_ = true; }
// Open the output file. FILE_SIZE is the final size of the file.
void
open(off_t file_size);
@ -2649,6 +2660,8 @@ class Output_file
unsigned char* base_;
// True iff base_ points to a memory buffer rather than an output file.
bool map_is_anonymous_;
// True if this is a temporary file which should not be output.
bool is_temporary_;
};
} // End namespace gold.