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())
|
if (!doing_static_link && parameters->output_is_object())
|
||||||
gold_error(_("cannot mix -r with dynamic object %s"),
|
gold_error(_("cannot mix -r with dynamic object %s"),
|
||||||
(*input_objects->dynobj_begin())->name().c_str());
|
(*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))
|
if (is_debugging_enabled(DEBUG_SCRIPT))
|
||||||
layout->script_options()->print(stderr);
|
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
|
// Queue a task to close the output file. This will be blocked by
|
||||||
// FINAL_BLOCKER.
|
// 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,
|
final_blocker,
|
||||||
"Task_function Close_task_runner"));
|
"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
|
// Now we know the final size of the output file and we know where
|
||||||
// each piece of information goes.
|
// each piece of information goes.
|
||||||
Output_file* of = new Output_file(parameters->output_file_name());
|
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);
|
of->open(file_size);
|
||||||
|
|
||||||
// Queue up the final set of tasks.
|
// Queue up the final set of tasks.
|
||||||
|
@ -949,6 +951,9 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
|
||||||
else
|
else
|
||||||
load_seg = this->find_first_load_seg();
|
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);
|
gold_assert(phdr_seg == NULL || load_seg != NULL);
|
||||||
|
|
||||||
// Lay out the segment headers.
|
// Lay out the segment headers.
|
||||||
|
@ -2486,6 +2491,55 @@ Layout::write_sections_after_input_sections(Output_file* of)
|
||||||
this->section_headers_->write(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.
|
// Print statistical information to stderr. This is used for --stats.
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -2617,6 +2671,10 @@ Write_after_input_sections_task::run(Workqueue*)
|
||||||
void
|
void
|
||||||
Close_task_runner::run(Workqueue*, const Task*)
|
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();
|
this->of_->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -287,6 +287,10 @@ class Layout
|
||||||
script_options() const
|
script_options() const
|
||||||
{ return this->script_options_; }
|
{ return this->script_options_; }
|
||||||
|
|
||||||
|
// Rewrite output file in binary format.
|
||||||
|
void
|
||||||
|
write_binary(Output_file* in) const;
|
||||||
|
|
||||||
// Dump statistical information to stderr.
|
// Dump statistical information to stderr.
|
||||||
void
|
void
|
||||||
print_stats() const;
|
print_stats() const;
|
||||||
|
@ -732,8 +736,9 @@ class Write_after_input_sections_task : public Task
|
||||||
class Close_task_runner : public Task_function_runner
|
class Close_task_runner : public Task_function_runner
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Close_task_runner(Output_file* of)
|
Close_task_runner(const General_options* options, const Layout* layout,
|
||||||
: of_(of)
|
Output_file* of)
|
||||||
|
: options_(options), layout_(layout), of_(of)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
// Run the operation.
|
// Run the operation.
|
||||||
|
@ -741,6 +746,8 @@ class Close_task_runner : public Task_function_runner
|
||||||
run(Workqueue*, const Task*);
|
run(Workqueue*, const Task*);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
const General_options* options_;
|
||||||
|
const Layout* layout_;
|
||||||
Output_file* of_;
|
Output_file* of_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -477,6 +477,9 @@ options::Command_line_options::options[] =
|
||||||
GENERAL_ARG('O', NULL, N_("Optimize output file size"),
|
GENERAL_ARG('O', NULL, N_("Optimize output file size"),
|
||||||
N_("-O level"), ONE_DASH,
|
N_("-O level"), ONE_DASH,
|
||||||
&General_options::set_optimization_level),
|
&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,
|
GENERAL_NOARG('r', NULL, N_("Generate relocatable output"), NULL,
|
||||||
ONE_DASH, &General_options::set_relocatable),
|
ONE_DASH, &General_options::set_relocatable),
|
||||||
// -R really means -rpath, but can mean --just-symbols for
|
// -R really means -rpath, but can mean --just-symbols for
|
||||||
|
@ -605,6 +608,7 @@ General_options::General_options(Script_options* script_options)
|
||||||
search_path_(),
|
search_path_(),
|
||||||
optimization_level_(0),
|
optimization_level_(0),
|
||||||
output_file_name_("a.out"),
|
output_file_name_("a.out"),
|
||||||
|
output_format_(OUTPUT_FORMAT_ELF),
|
||||||
is_relocatable_(false),
|
is_relocatable_(false),
|
||||||
strip_(STRIP_NONE),
|
strip_(STRIP_NONE),
|
||||||
allow_shlib_undefined_(false),
|
allow_shlib_undefined_(false),
|
||||||
|
@ -643,6 +647,24 @@ General_options::define_symbol(const char* arg)
|
||||||
this->script_options_->define_symbol(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.
|
// Handle the -z option.
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -855,8 +877,8 @@ Command_line::process_one_option(int argc, char** argv, int i,
|
||||||
{
|
{
|
||||||
if (options[j].long_option != NULL
|
if (options[j].long_option != NULL
|
||||||
&& (dashes == 2
|
&& (dashes == 2
|
||||||
|| (options[j].dash
|
|| (options[j].dash
|
||||||
!= options::One_option::EXACTLY_TWO_DASHES))
|
!= options::One_option::EXACTLY_TWO_DASHES))
|
||||||
&& first == options[j].long_option[0]
|
&& first == options[j].long_option[0]
|
||||||
&& strcmp(opt, 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())
|
if (this->options_.is_shared() && this->options_.is_relocatable())
|
||||||
gold_fatal(_("-shared and -r are incompatible"));
|
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.
|
// If the user specifies both -s and -r, convert the -s as -S.
|
||||||
// -r requires us to keep externally visible symbols!
|
// -r requires us to keep externally visible symbols!
|
||||||
if (this->options_.strip_all() && this->options_.is_relocatable())
|
if (this->options_.strip_all() && this->options_.is_relocatable())
|
||||||
|
|
|
@ -148,6 +148,20 @@ class General_options
|
||||||
output_file_name() const
|
output_file_name() const
|
||||||
{ return this->output_file_name_; }
|
{ 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.
|
// -r: Whether we are doing a relocatable link.
|
||||||
bool
|
bool
|
||||||
is_relocatable() const
|
is_relocatable() const
|
||||||
|
@ -370,6 +384,9 @@ class General_options
|
||||||
set_output_file_name(const char* arg)
|
set_output_file_name(const char* arg)
|
||||||
{ this->output_file_name_ = arg; }
|
{ this->output_file_name_ = arg; }
|
||||||
|
|
||||||
|
void
|
||||||
|
set_output_format(const char*);
|
||||||
|
|
||||||
void
|
void
|
||||||
set_relocatable()
|
set_relocatable()
|
||||||
{ this->is_relocatable_ = true; }
|
{ this->is_relocatable_ = true; }
|
||||||
|
@ -544,6 +561,7 @@ class General_options
|
||||||
Dir_list search_path_;
|
Dir_list search_path_;
|
||||||
int optimization_level_;
|
int optimization_level_;
|
||||||
const char* output_file_name_;
|
const char* output_file_name_;
|
||||||
|
Output_format output_format_;
|
||||||
bool is_relocatable_;
|
bool is_relocatable_;
|
||||||
Strip strip_;
|
Strip strip_;
|
||||||
bool allow_shlib_undefined_;
|
bool allow_shlib_undefined_;
|
||||||
|
|
|
@ -2755,7 +2755,8 @@ Output_file::Output_file(const char* name)
|
||||||
o_(-1),
|
o_(-1),
|
||||||
file_size_(0),
|
file_size_(0),
|
||||||
base_(NULL),
|
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().
|
// to improve the odds for open().
|
||||||
|
|
||||||
// We let the name "-" mean "stdout"
|
// We let the name "-" mean "stdout"
|
||||||
if (strcmp(this->name_, "-") == 0)
|
if (!this->is_temporary_)
|
||||||
this->o_ = STDOUT_FILENO;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
struct stat s;
|
if (strcmp(this->name_, "-") == 0)
|
||||||
if (::stat(this->name_, &s) == 0 && s.st_size != 0)
|
this->o_ = STDOUT_FILENO;
|
||||||
unlink_if_ordinary(this->name_);
|
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 mode = parameters->output_is_object() ? 0666 : 0777;
|
||||||
int o = ::open(this->name_, O_RDWR | O_CREAT | O_TRUNC, mode);
|
int o = ::open(this->name_, O_RDWR | O_CREAT | O_TRUNC, mode);
|
||||||
if (o < 0)
|
if (o < 0)
|
||||||
gold_fatal(_("%s: open: %s"), this->name_, strerror(errno));
|
gold_fatal(_("%s: open: %s"), this->name_, strerror(errno));
|
||||||
this->o_ = o;
|
this->o_ = o;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->map();
|
this->map();
|
||||||
|
@ -2837,7 +2841,8 @@ Output_file::map()
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
if (o == STDOUT_FILENO || o == STDERR_FILENO
|
if (o == STDOUT_FILENO || o == STDERR_FILENO
|
||||||
|| ::fstat(o, &statbuf) != 0
|
|| ::fstat(o, &statbuf) != 0
|
||||||
|| !S_ISREG(statbuf.st_mode))
|
|| !S_ISREG(statbuf.st_mode)
|
||||||
|
|| this->is_temporary_)
|
||||||
{
|
{
|
||||||
this->map_is_anonymous_ = true;
|
this->map_is_anonymous_ = true;
|
||||||
base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
|
base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
|
||||||
|
@ -2878,7 +2883,7 @@ void
|
||||||
Output_file::close()
|
Output_file::close()
|
||||||
{
|
{
|
||||||
// If the map isn't file-backed, we need to write it now.
|
// 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_;
|
size_t bytes_to_write = this->file_size_;
|
||||||
while (bytes_to_write > 0)
|
while (bytes_to_write > 0)
|
||||||
|
@ -2895,7 +2900,9 @@ Output_file::close()
|
||||||
this->unmap();
|
this->unmap();
|
||||||
|
|
||||||
// We don't close stdout or stderr
|
// 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)
|
if (::close(this->o_) < 0)
|
||||||
gold_error(_("%s: close: %s"), this->name_, strerror(errno));
|
gold_error(_("%s: close: %s"), this->name_, strerror(errno));
|
||||||
this->o_ = -1;
|
this->o_ = -1;
|
||||||
|
|
|
@ -2393,6 +2393,11 @@ class Output_segment
|
||||||
filesz() const
|
filesz() const
|
||||||
{ return this->filesz_; }
|
{ return this->filesz_; }
|
||||||
|
|
||||||
|
// Return the file offset.
|
||||||
|
off_t
|
||||||
|
offset() const
|
||||||
|
{ return this->offset_; }
|
||||||
|
|
||||||
// Return the maximum alignment of the Output_data.
|
// Return the maximum alignment of the Output_data.
|
||||||
uint64_t
|
uint64_t
|
||||||
maximum_alignment();
|
maximum_alignment();
|
||||||
|
@ -2571,6 +2576,12 @@ class Output_file
|
||||||
public:
|
public:
|
||||||
Output_file(const char* name);
|
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.
|
// Open the output file. FILE_SIZE is the final size of the file.
|
||||||
void
|
void
|
||||||
open(off_t file_size);
|
open(off_t file_size);
|
||||||
|
@ -2649,6 +2660,8 @@ class Output_file
|
||||||
unsigned char* base_;
|
unsigned char* base_;
|
||||||
// True iff base_ points to a memory buffer rather than an output file.
|
// True iff base_ points to a memory buffer rather than an output file.
|
||||||
bool map_is_anonymous_;
|
bool map_is_anonymous_;
|
||||||
|
// True if this is a temporary file which should not be output.
|
||||||
|
bool is_temporary_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End namespace gold.
|
} // End namespace gold.
|
||||||
|
|
Loading…
Reference in New Issue