diff --git a/gold/gold.cc b/gold/gold.cc index f067557cb2..d09dee3086 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 98617f97fe..cea25b0193 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 674956e688..e4e97e43bd 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 420f795201..b660ed0cbb 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 dbfb3bd15a..461a062617 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 @@ -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_; diff --git a/gold/output.cc b/gold/output.cc index 332aa2cf2c..8aca916be0 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_); + 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; diff --git a/gold/output.h b/gold/output.h index 7333513d6e..5e68e7c294 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.