| // mapfile.cc -- map file generation for gold |
| |
| // Copyright (C) 2008-2024 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]; |
| section_size_type size; |
| if (!relobj->section_is_compressed(shndx, &size)) |
| size = relobj->section_size(shndx); |
| snprintf(sizebuf, sizeof sizebuf, "0x%llx", |
| static_cast<unsigned long long>(size)); |
| |
| 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->current_data_size())); |
| |
| fprintf(this->map_file_, "0x%0*llx %10s\n", |
| parameters->target().get_size() / 4, |
| (od->is_address_valid() |
| ? static_cast<unsigned long long>(od->address()) |
| : 0), |
| 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->current_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())); |
| |
| if (os->requires_postprocessing()) |
| fprintf(this->map_file_, " (before compression)"); |
| |
| putc('\n', this->map_file_); |
| } |
| |
| } // End namespace gold. |