Support --oformat binary.
This commit is contained in:
parent
1ef1f3d387
commit
516cb3d01e
@ -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"));
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user