aboutsummaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
Diffstat (limited to 'gold')
-rw-r--r--gold/gold.cc7
-rw-r--r--gold/layout.cc58
-rw-r--r--gold/layout.h11
-rw-r--r--gold/options.cc30
-rw-r--r--gold/options.h18
-rw-r--r--gold/output.cc39
-rw-r--r--gold/output.h13
7 files changed, 155 insertions, 21 deletions
diff --git a/gold/gold.cc b/gold/gold.cc
index f067557..d09dee3 100644
--- a/gold/gold.cc
+++ b/gold/gold.cc
@@ -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"));
}
diff --git a/gold/layout.cc b/gold/layout.cc
index 98617f9..cea25b0 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -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();
}
diff --git a/gold/layout.h b/gold/layout.h
index 674956e..e4e97e4 100644
--- a/gold/layout.h
+++ b/gold/layout.h
@@ -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_;
};
diff --git a/gold/options.cc b/gold/options.cc
index 420f795..b660ed0 100644
--- a/gold/options.cc
+++ b/gold/options.cc
@@ -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())
diff --git a/gold/options.h b/gold/options.h
index dbfb3bd..461a062 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -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
@@ -371,6 +385,9 @@ class General_options
{ 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_;
diff --git a/gold/output.cc b/gold/output.cc
index 332aa2c..8aca916 100644
--- a/gold/output.cc
+++ b/gold/output.cc
@@ -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_);
-
- 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;
+ 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;
+ }
}
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;
diff --git a/gold/output.h b/gold/output.h
index 7333513..5e68e7c 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -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.