// mapfile.cc -- map file generation for gold // Copyright 2008 Free Software Foundation, Inc. // Written by Ian Lance Taylor <iant@google.com>. // This file is part of gold. // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, // MA 02110-1301, USA. #include "gold.h" #include <cerrno> #include <cstdio> #include <cstring> #include "archive.h" #include "symtab.h" #include "output.h" #include "mapfile.h" // This file holds the code for printing information to the map file. // In general we try to produce pretty much the same format as GNU ld. namespace gold { // Mapfile constructor. Mapfile::Mapfile() : map_file_(NULL), printed_archive_header_(false), printed_common_header_(false), printed_memory_map_header_(false) { } // Mapfile destructor. Mapfile::~Mapfile() { if (this->map_file_ != NULL) this->close(); } // Open the map file. bool Mapfile::open(const char* map_filename) { if (strcmp(map_filename, "-") == 0) this->map_file_ = stdout; else { this->map_file_ = ::fopen(map_filename, "w"); if (this->map_file_ == NULL) { gold_error(_("cannot open map file %s: %s"), map_filename, strerror(errno)); return false; } } return true; } // Close the map file. void Mapfile::close() { if (fclose(this->map_file_) != 0) gold_error(_("cannot close map file: %s"), strerror(errno)); this->map_file_ = NULL; } // Advance to a column. void Mapfile::advance_to_column(size_t from, size_t to) { if (from >= to - 1) { putc('\n', this->map_file_); from = 0; } while (from < to) { putc(' ', this->map_file_); ++from; } } // Report about including a member from an archive. void Mapfile::report_include_archive_member(const std::string& member_name, const Symbol* sym, const char* why) { // We print a header before the list of archive members, mainly for // GNU ld compatibility. if (!this->printed_archive_header_) { fprintf(this->map_file_, _("Archive member included because of file (symbol)\n\n")); this->printed_archive_header_ = true; } fprintf(this->map_file_, "%s", member_name.c_str()); this->advance_to_column(member_name.length(), 30); if (sym == NULL) fprintf(this->map_file_, "%s", why); else { switch (sym->source()) { case Symbol::FROM_OBJECT: fprintf(this->map_file_, "%s", sym->object()->name().c_str()); break; case Symbol::IS_UNDEFINED: fprintf(this->map_file_, "-u"); break; default: case Symbol::IN_OUTPUT_DATA: case Symbol::IN_OUTPUT_SEGMENT: case Symbol::IS_CONSTANT: // We should only see an undefined symbol here. gold_unreachable(); } fprintf(this->map_file_, " (%s)", sym->name()); } putc('\n', this->map_file_); } // Report allocating a common symbol. void Mapfile::report_allocate_common(const Symbol* sym, uint64_t symsize) { if (!this->printed_common_header_) { fprintf(this->map_file_, _("\nAllocating common symbols\n")); fprintf(this->map_file_, _("Common symbol size file\n\n")); this->printed_common_header_ = true; } std::string demangled_name = sym->demangled_name(); fprintf(this->map_file_, "%s", demangled_name.c_str()); this->advance_to_column(demangled_name.length(), 20); char buf[50]; snprintf(buf, sizeof buf, "0x%llx", static_cast<unsigned long long>(symsize)); fprintf(this->map_file_, "%s", buf); size_t len = strlen(buf); while (len < 18) { putc(' ', this->map_file_); ++len; } fprintf(this->map_file_, "%s\n", sym->object()->name().c_str()); } // The space we make for a section name. const size_t Mapfile::section_name_map_length = 16; // Print the memory map header if necessary. void Mapfile::print_memory_map_header() { if (!this->printed_memory_map_header_) { fprintf(this->map_file_, _("\nMemory map\n\n")); this->printed_memory_map_header_ = true; } } // Print the symbols associated with an input section. template<int size, bool big_endian> void Mapfile::print_input_section_symbols( const Sized_relobj_file<size, big_endian>* relobj, unsigned int shndx) { unsigned int symcount = relobj->symbol_count(); for (unsigned int i = relobj->local_symbol_count(); i < symcount; ++i) { const Symbol* sym = relobj->global_symbol(i); bool is_ordinary; if (sym != NULL && sym->source() == Symbol::FROM_OBJECT && sym->object() == relobj && sym->shndx(&is_ordinary) == shndx && is_ordinary && sym->is_defined()) { for (size_t i = 0; i < Mapfile::section_name_map_length; ++i) putc(' ', this->map_file_); const Sized_symbol<size>* ssym = static_cast<const Sized_symbol<size>*>(sym); fprintf(this->map_file_, "0x%0*llx %s\n", size / 4, static_cast<unsigned long long>(ssym->value()), sym->demangled_name().c_str()); } } } // Print an input section. void Mapfile::print_input_section(Relobj* relobj, unsigned int shndx) { putc(' ', this->map_file_); std::string name = relobj->section_name(shndx); fprintf(this->map_file_, "%s", name.c_str()); this->advance_to_column(name.length() + 1, Mapfile::section_name_map_length); Output_section* os; uint64_t addr; if (!relobj->is_section_included(shndx)) { os = NULL; addr = 0; } else { os = relobj->output_section(shndx); addr = relobj->output_section_offset(shndx); if (addr != -1ULL) addr += os->address(); } char sizebuf[50]; snprintf(sizebuf, sizeof sizebuf, "0x%llx", static_cast<unsigned long long>(relobj->section_size(shndx))); fprintf(this->map_file_, "0x%0*llx %10s %s\n", parameters->target().get_size() / 4, static_cast<unsigned long long>(addr), sizebuf, relobj->name().c_str()); if (os != NULL) { switch (parameters->size_and_endianness()) { #ifdef HAVE_TARGET_32_LITTLE case Parameters::TARGET_32_LITTLE: { const Sized_relobj_file<32, false>* sized_relobj = static_cast<Sized_relobj_file<32, false>*>(relobj); this->print_input_section_symbols(sized_relobj, shndx); } break; #endif #ifdef HAVE_TARGET_32_BIG case Parameters::TARGET_32_BIG: { const Sized_relobj_file<32, true>* sized_relobj = static_cast<Sized_relobj_file<32, true>*>(relobj); this->print_input_section_symbols(sized_relobj, shndx); } break; #endif #ifdef HAVE_TARGET_64_LITTLE case Parameters::TARGET_64_LITTLE: { const Sized_relobj_file<64, false>* sized_relobj = static_cast<Sized_relobj_file<64, false>*>(relobj); this->print_input_section_symbols(sized_relobj, shndx); } break; #endif #ifdef HAVE_TARGET_64_BIG case Parameters::TARGET_64_BIG: { const Sized_relobj_file<64, true>* sized_relobj = static_cast<Sized_relobj_file<64, true>*>(relobj); this->print_input_section_symbols(sized_relobj, shndx); } break; #endif default: gold_unreachable(); } } } // Print an Output_section_data. This is printed to look like an // input section. void Mapfile::print_output_data(const Output_data* od, const char* name) { this->print_memory_map_header(); putc(' ', this->map_file_); fprintf(this->map_file_, "%s", name); this->advance_to_column(strlen(name) + 1, Mapfile::section_name_map_length); char sizebuf[50]; snprintf(sizebuf, sizeof sizebuf, "0x%llx", static_cast<unsigned long long>(od->data_size())); fprintf(this->map_file_, "0x%0*llx %10s\n", parameters->target().get_size() / 4, static_cast<unsigned long long>(od->address()), sizebuf); } // Print the discarded input sections. void Mapfile::print_discarded_sections(const Input_objects* input_objects) { bool printed_header = false; for (Input_objects::Relobj_iterator p = input_objects->relobj_begin(); p != input_objects->relobj_end(); ++p) { Relobj* relobj = *p; // Lock the object so we can read from it. This is only called // single-threaded from Layout_task_runner, so it is OK to lock. // Unfortunately we have no way to pass in a Task token. const Task* dummy_task = reinterpret_cast<const Task*>(-1); Task_lock_obj<Object> tl(dummy_task, relobj); unsigned int shnum = relobj->shnum(); for (unsigned int i = 0; i < shnum; ++i) { unsigned int sh_type = relobj->section_type(i); if ((sh_type == elfcpp::SHT_PROGBITS || sh_type == elfcpp::SHT_NOBITS || sh_type == elfcpp::SHT_GROUP) && !relobj->is_section_included(i)) { if (!printed_header) { fprintf(this->map_file_, _("\nDiscarded input sections\n\n")); printed_header = true; } this->print_input_section(relobj, i); } } } } // Print an output section. void Mapfile::print_output_section(const Output_section* os) { this->print_memory_map_header(); fprintf(this->map_file_, "\n%s", os->name()); this->advance_to_column(strlen(os->name()), Mapfile::section_name_map_length); char sizebuf[50]; snprintf(sizebuf, sizeof sizebuf, "0x%llx", static_cast<unsigned long long>(os->data_size())); fprintf(this->map_file_, "0x%0*llx %10s", parameters->target().get_size() / 4, static_cast<unsigned long long>(os->address()), sizebuf); if (os->has_load_address()) fprintf(this->map_file_, " load address 0x%-*llx", parameters->target().get_size() / 4, static_cast<unsigned long long>(os->load_address())); putc('\n', this->map_file_); } } // End namespace gold.