Support --oformat binary.
authorIan Lance Taylor <iant@google.com>
Thu, 7 Feb 2008 07:33:46 +0000 (07:33 +0000)
committerIan Lance Taylor <iant@google.com>
Thu, 7 Feb 2008 07:33:46 +0000 (07:33 +0000)
gold/gold.cc
gold/layout.cc
gold/layout.h
gold/options.cc
gold/options.h
gold/output.cc
gold/output.h

index f067557cb249b2254ecbd34db2a6c33f5ef09dd2..d09dee30866cdc5a47fc8c0b3dade21c7e3bfb10 100644 (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"));
 }
index 98617f97fed8e2293bc6a83d07862f0ed712c8cf..cea25b0193e2c13748a99cc941049d25100b548b 100644 (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();
 }
 
index 674956e68891751fac9a47569897b33ad0016370..e4e97e43bd744af290c38b323bc38b3057a7b26e 100644 (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_;
 };
 
index 420f795201b3f5ccccdee7e9269296da13810240..b660ed0cbb8a42287b288fe090c2ebe80c9a9da6 100644 (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())
index dbfb3bd15adda9e41932f081a8b82454b9336db4..461a06261772569af2b72217438ae75e34c6c14e 100644 (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_;
index 332aa2cf2c687be9d284b24fdf1449d377a58dd2..8aca916be0bea6a202effc92f0e8b591b787a8db 100644 (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_);
-
-      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;
index 7333513d6e1e43915c1a1a376a168b454f23dc8a..5e68e7c294ae238a749b7afedb593eb604f0e031 100644 (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.