|  | // fileread.h -- read files for gold   -*- C++ -*- | 
|  |  | 
|  | // Copyright (C) 2006-2023 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. | 
|  |  | 
|  | // Classes used to read data from binary input files. | 
|  |  | 
|  | #ifndef GOLD_FILEREAD_H | 
|  | #define GOLD_FILEREAD_H | 
|  |  | 
|  | #include <list> | 
|  | #include <map> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "token.h" | 
|  |  | 
|  | namespace gold | 
|  | { | 
|  |  | 
|  | // Since not all system supports stat.st_mtim and struct timespec, | 
|  | // we define our own structure and fill the nanoseconds if we can. | 
|  |  | 
|  | struct Timespec | 
|  | { | 
|  | Timespec() | 
|  | : seconds(0), nanoseconds(0) | 
|  | { } | 
|  |  | 
|  | Timespec(time_t a_seconds, int a_nanoseconds) | 
|  | : seconds(a_seconds), nanoseconds(a_nanoseconds) | 
|  | { } | 
|  |  | 
|  | time_t seconds; | 
|  | int nanoseconds; | 
|  | }; | 
|  |  | 
|  | // Get the last modified time of an unopened file.  Returns false if the | 
|  | // file does not exist. | 
|  |  | 
|  | bool | 
|  | get_mtime(const char* filename, Timespec* mtime); | 
|  |  | 
|  | class Position_dependent_options; | 
|  | class Input_file_argument; | 
|  | class Dirsearch; | 
|  | class File_view; | 
|  |  | 
|  | // File_read manages a file descriptor and mappings for a file we are | 
|  | // reading. | 
|  |  | 
|  | class File_read | 
|  | { | 
|  | public: | 
|  | File_read() | 
|  | : name_(), descriptor_(-1), is_descriptor_opened_(false), object_count_(0), | 
|  | size_(0), token_(false), views_(), saved_views_(), mapped_bytes_(0), | 
|  | released_(true), whole_file_view_(NULL) | 
|  | { } | 
|  |  | 
|  | ~File_read(); | 
|  |  | 
|  | // Open a file. | 
|  | bool | 
|  | open(const Task*, const std::string& name); | 
|  |  | 
|  | // Pretend to open the file, but provide the file contents.  No | 
|  | // actual file system activity will occur.  This is used for | 
|  | // testing. | 
|  | bool | 
|  | open(const Task*, const std::string& name, const unsigned char* contents, | 
|  | off_t size); | 
|  |  | 
|  | // Return the file name. | 
|  | const std::string& | 
|  | filename() const | 
|  | { return this->name_; } | 
|  |  | 
|  | // Add an object associated with a file. | 
|  | void | 
|  | add_object() | 
|  | { ++this->object_count_; } | 
|  |  | 
|  | // Remove an object associated with a file. | 
|  | void | 
|  | remove_object() | 
|  | { --this->object_count_; } | 
|  |  | 
|  | // Lock the file for exclusive access within a particular Task::run | 
|  | // execution.  This routine may only be called when the workqueue | 
|  | // lock is held. | 
|  | void | 
|  | lock(const Task* t); | 
|  |  | 
|  | // Unlock the file. | 
|  | void | 
|  | unlock(const Task* t); | 
|  |  | 
|  | // Test whether the object is locked. | 
|  | bool | 
|  | is_locked() const; | 
|  |  | 
|  | // Return the token, so that the task can be queued. | 
|  | Task_token* | 
|  | token() | 
|  | { return &this->token_; } | 
|  |  | 
|  | // Release the file.  This indicates that we aren't going to do | 
|  | // anything further with it until it is unlocked.  This is used | 
|  | // because a Task which locks the file never calls either lock or | 
|  | // unlock; it just locks the token.  The basic rule is that a Task | 
|  | // which locks a file via the Task::locks interface must explicitly | 
|  | // call release() when it is done.  This is not necessary for code | 
|  | // which calls unlock() on the file. | 
|  | void | 
|  | release(); | 
|  |  | 
|  | // Return the size of the file. | 
|  | off_t | 
|  | filesize() const | 
|  | { return this->size_; } | 
|  |  | 
|  | // Return a view into the file starting at file offset START for | 
|  | // SIZE bytes.  OFFSET is the offset into the input file for the | 
|  | // file we are reading; this is zero for a normal object file, | 
|  | // non-zero for an object file in an archive.  ALIGNED is true if | 
|  | // the data must be naturally aligned (i.e., aligned to the size | 
|  | // of a target word); this only matters when OFFSET is not zero. | 
|  | // The pointer will remain valid until the File_read is unlocked. | 
|  | // It is an error if we can not read enough data from the file. | 
|  | // The CACHE parameter is a hint as to whether it will be useful | 
|  | // to cache this data for later accesses--i.e., later calls to | 
|  | // get_view, read, or get_lasting_view which retrieve the same | 
|  | // data. | 
|  | const unsigned char* | 
|  | get_view(off_t offset, off_t start, section_size_type size, bool aligned, | 
|  | bool cache); | 
|  |  | 
|  | // Read data from the file into the buffer P starting at file offset | 
|  | // START for SIZE bytes. | 
|  | void | 
|  | read(off_t start, section_size_type size, void* p); | 
|  |  | 
|  | // Return a lasting view into the file starting at file offset START | 
|  | // for SIZE bytes.  This is allocated with new, and the caller is | 
|  | // responsible for deleting it when done.  The data associated with | 
|  | // this view will remain valid until the view is deleted.  It is an | 
|  | // error if we can not read enough data from the file.  The OFFSET, | 
|  | // ALIGNED and CACHE parameters are as in get_view. | 
|  | File_view* | 
|  | get_lasting_view(off_t offset, off_t start, section_size_type size, | 
|  | bool aligned, bool cache); | 
|  |  | 
|  | // Mark all views as no longer cached. | 
|  | void | 
|  | clear_view_cache_marks(); | 
|  |  | 
|  | // Discard all uncached views.  This is normally done by release(), | 
|  | // but not for objects in archives.  FIXME: This is a complicated | 
|  | // interface, and it would be nice to have something more automatic. | 
|  | void | 
|  | clear_uncached_views() | 
|  | { this->clear_views(CLEAR_VIEWS_ARCHIVE); } | 
|  |  | 
|  | // A struct used to do a multiple read. | 
|  | struct Read_multiple_entry | 
|  | { | 
|  | // The file offset of the data to read. | 
|  | off_t file_offset; | 
|  | // The amount of data to read. | 
|  | section_size_type size; | 
|  | // The buffer where the data should be placed. | 
|  | unsigned char* buffer; | 
|  |  | 
|  | Read_multiple_entry(off_t o, section_size_type s, unsigned char* b) | 
|  | : file_offset(o), size(s), buffer(b) | 
|  | { } | 
|  | }; | 
|  |  | 
|  | typedef std::vector<Read_multiple_entry> Read_multiple; | 
|  |  | 
|  | // Read a bunch of data from the file into various different | 
|  | // locations.  The vector must be sorted by ascending file_offset. | 
|  | // BASE is a base offset to be added to all the offsets in the | 
|  | // vector. | 
|  | void | 
|  | read_multiple(off_t base, const Read_multiple&); | 
|  |  | 
|  | // Dump statistical information to stderr. | 
|  | static void | 
|  | print_stats(); | 
|  |  | 
|  | // Write the dependency file listing all files read. | 
|  | static void | 
|  | write_dependency_file(const char* dependency_file_name, | 
|  | const char* output_file_name); | 
|  |  | 
|  | // Record that a file was read.  File_read::open does this. | 
|  | static void | 
|  | record_file_read(const std::string& name); | 
|  |  | 
|  | // Return the open file descriptor (for plugins). | 
|  | int | 
|  | descriptor() | 
|  | { | 
|  | this->reopen_descriptor(); | 
|  | return this->descriptor_; | 
|  | } | 
|  |  | 
|  | // Return the file last modification time.  Calls gold_fatal if the stat | 
|  | // system call failed. | 
|  | Timespec | 
|  | get_mtime(); | 
|  |  | 
|  | private: | 
|  | // Control for what views to clear. | 
|  | enum Clear_views_mode | 
|  | { | 
|  | // Clear uncached views not used by an archive. | 
|  | CLEAR_VIEWS_NORMAL, | 
|  | // Clear all uncached views (including in an archive). | 
|  | CLEAR_VIEWS_ARCHIVE, | 
|  | // Clear all views (i.e., we're destroying the file). | 
|  | CLEAR_VIEWS_ALL | 
|  | }; | 
|  |  | 
|  | // This class may not be copied. | 
|  | File_read(const File_read&); | 
|  | File_read& operator=(const File_read&); | 
|  |  | 
|  | // Total bytes mapped into memory during the link if --stats. | 
|  | static unsigned long long total_mapped_bytes; | 
|  |  | 
|  | // Current number of bytes mapped into memory during the link if | 
|  | // --stats. | 
|  | static unsigned long long current_mapped_bytes; | 
|  |  | 
|  | // High water mark of bytes mapped into memory during the link if | 
|  | // --stats. | 
|  | static unsigned long long maximum_mapped_bytes; | 
|  |  | 
|  | // Set of names of all files read. | 
|  | static std::vector<std::string> files_read; | 
|  |  | 
|  | // A view into the file. | 
|  | class View | 
|  | { | 
|  | public: | 
|  | // Specifies how to dispose the data on destruction of the view. | 
|  | enum Data_ownership | 
|  | { | 
|  | // Data owned by File object - nothing done in destructor. | 
|  | DATA_NOT_OWNED, | 
|  | // Data allocated with new[] and owned by this object - should | 
|  | // use delete[]. | 
|  | DATA_ALLOCATED_ARRAY, | 
|  | // Data mmapped and owned by this object - should munmap. | 
|  | DATA_MMAPPED | 
|  | }; | 
|  |  | 
|  | View(off_t start, section_size_type size, const unsigned char* data, | 
|  | unsigned int byteshift, bool cache, Data_ownership data_ownership) | 
|  | : start_(start), size_(size), data_(data), lock_count_(0), | 
|  | byteshift_(byteshift), cache_(cache), data_ownership_(data_ownership), | 
|  | accessed_(true) | 
|  | { } | 
|  |  | 
|  | ~View(); | 
|  |  | 
|  | off_t | 
|  | start() const | 
|  | { return this->start_; } | 
|  |  | 
|  | section_size_type | 
|  | size() const | 
|  | { return this->size_; } | 
|  |  | 
|  | const unsigned char* | 
|  | data() const | 
|  | { return this->data_; } | 
|  |  | 
|  | void | 
|  | lock(); | 
|  |  | 
|  | void | 
|  | unlock(); | 
|  |  | 
|  | bool | 
|  | is_locked(); | 
|  |  | 
|  | unsigned int | 
|  | byteshift() const | 
|  | { return this->byteshift_; } | 
|  |  | 
|  | void | 
|  | set_cache() | 
|  | { this->cache_ = true; } | 
|  |  | 
|  | void | 
|  | clear_cache() | 
|  | { this->cache_ = false; } | 
|  |  | 
|  | bool | 
|  | should_cache() const | 
|  | { return this->cache_; } | 
|  |  | 
|  | void | 
|  | set_accessed() | 
|  | { this->accessed_ = true; } | 
|  |  | 
|  | void | 
|  | clear_accessed() | 
|  | { this->accessed_= false; } | 
|  |  | 
|  | bool | 
|  | accessed() const | 
|  | { return this->accessed_; } | 
|  |  | 
|  | // Returns TRUE if this view contains permanent data -- e.g., data that | 
|  | // was supplied by the owner of the File object. | 
|  | bool | 
|  | is_permanent_view() const | 
|  | { return this->data_ownership_ == DATA_NOT_OWNED; } | 
|  |  | 
|  | private: | 
|  | View(const View&); | 
|  | View& operator=(const View&); | 
|  |  | 
|  | // The file offset of the start of the view. | 
|  | off_t start_; | 
|  | // The size of the view. | 
|  | section_size_type size_; | 
|  | // A pointer to the actual bytes. | 
|  | const unsigned char* data_; | 
|  | // The number of locks on this view. | 
|  | int lock_count_; | 
|  | // The number of bytes that the view is shifted relative to the | 
|  | // underlying file.  This is used to align data.  This is normally | 
|  | // zero, except possibly for an object in an archive. | 
|  | unsigned int byteshift_; | 
|  | // Whether the view is cached. | 
|  | bool cache_; | 
|  | // Whether the view is mapped into memory.  If not, data_ points | 
|  | // to memory allocated using new[]. | 
|  | Data_ownership data_ownership_; | 
|  | // Whether the view has been accessed recently. | 
|  | bool accessed_; | 
|  | }; | 
|  |  | 
|  | friend class View; | 
|  | friend class File_view; | 
|  |  | 
|  | // The type of a mapping from page start and byte shift to views. | 
|  | typedef std::map<std::pair<off_t, unsigned int>, View*> Views; | 
|  |  | 
|  | // A simple list of Views. | 
|  | typedef std::list<View*> Saved_views; | 
|  |  | 
|  | // Open the descriptor if necessary. | 
|  | void | 
|  | reopen_descriptor(); | 
|  |  | 
|  | // Find a view into the file. | 
|  | View* | 
|  | find_view(off_t start, section_size_type size, unsigned int byteshift, | 
|  | View** vshifted) const; | 
|  |  | 
|  | // Read data from the file into a buffer. | 
|  | void | 
|  | do_read(off_t start, section_size_type size, void* p); | 
|  |  | 
|  | // Add a view. | 
|  | void | 
|  | add_view(View*); | 
|  |  | 
|  | // Make a view into the file. | 
|  | View* | 
|  | make_view(off_t start, section_size_type size, unsigned int byteshift, | 
|  | bool cache); | 
|  |  | 
|  | // Find or make a view into the file. | 
|  | View* | 
|  | find_or_make_view(off_t offset, off_t start, section_size_type size, | 
|  | bool aligned, bool cache); | 
|  |  | 
|  | // Clear the file views. | 
|  | void | 
|  | clear_views(Clear_views_mode); | 
|  |  | 
|  | // The size of a file page for buffering data. | 
|  | static const off_t page_size = 8192; | 
|  |  | 
|  | // Given a file offset, return the page offset. | 
|  | static off_t | 
|  | page_offset(off_t file_offset) | 
|  | { return file_offset & ~ (page_size - 1); } | 
|  |  | 
|  | // Given a file size, return the size to read integral pages. | 
|  | static off_t | 
|  | pages(off_t file_size) | 
|  | { return (file_size + (page_size - 1)) & ~ (page_size - 1); } | 
|  |  | 
|  | // The maximum number of entries we will pass to ::readv. | 
|  | static const size_t max_readv_entries = 128; | 
|  |  | 
|  | // Use readv to read data. | 
|  | void | 
|  | do_readv(off_t base, const Read_multiple&, size_t start, size_t count); | 
|  |  | 
|  | // File name. | 
|  | std::string name_; | 
|  | // File descriptor. | 
|  | int descriptor_; | 
|  | // Whether we have regained the descriptor after releasing the file. | 
|  | bool is_descriptor_opened_; | 
|  | // The number of objects associated with this file.  This will be | 
|  | // more than 1 in the case of an archive. | 
|  | int object_count_; | 
|  | // File size. | 
|  | off_t size_; | 
|  | // A token used to lock the file. | 
|  | Task_token token_; | 
|  | // Buffered views into the file. | 
|  | Views views_; | 
|  | // List of views which were locked but had to be removed from views_ | 
|  | // because they were not large enough. | 
|  | Saved_views saved_views_; | 
|  | // Total amount of space mapped into memory.  This is only changed | 
|  | // while the file is locked.  When we unlock the file, we transfer | 
|  | // the total to total_mapped_bytes, and reset this to zero. | 
|  | size_t mapped_bytes_; | 
|  | // Whether the file was released. | 
|  | bool released_; | 
|  | // A view containing the whole file.  May be NULL if we mmap only | 
|  | // the relevant parts of the file.  Not NULL if: | 
|  | // - Flag --mmap_whole_files is set (default on 64-bit hosts). | 
|  | // - The contents was specified in the constructor.  Used only for | 
|  | //   testing purposes). | 
|  | View* whole_file_view_; | 
|  | }; | 
|  |  | 
|  | // A view of file data that persists even when the file is unlocked. | 
|  | // Callers should destroy these when no longer required.  These are | 
|  | // obtained form File_read::get_lasting_view.  They may only be | 
|  | // destroyed when the underlying File_read is locked. | 
|  |  | 
|  | class File_view | 
|  | { | 
|  | public: | 
|  | // This may only be called when the underlying File_read is locked. | 
|  | ~File_view(); | 
|  |  | 
|  | // Return a pointer to the data associated with this view. | 
|  | const unsigned char* | 
|  | data() const | 
|  | { return this->data_; } | 
|  |  | 
|  | private: | 
|  | File_view(const File_view&); | 
|  | File_view& operator=(const File_view&); | 
|  |  | 
|  | friend class File_read; | 
|  |  | 
|  | // Callers have to get these via File_read::get_lasting_view. | 
|  | File_view(File_read& file, File_read::View* view, const unsigned char* data) | 
|  | : file_(file), view_(view), data_(data) | 
|  | { } | 
|  |  | 
|  | File_read& file_; | 
|  | File_read::View* view_; | 
|  | const unsigned char* data_; | 
|  | }; | 
|  |  | 
|  | // All the information we hold for a single input file.  This can be | 
|  | // an object file, a shared library, or an archive. | 
|  |  | 
|  | class Input_file | 
|  | { | 
|  | public: | 
|  | enum Format | 
|  | { | 
|  | FORMAT_NONE, | 
|  | FORMAT_ELF, | 
|  | FORMAT_BINARY | 
|  | }; | 
|  |  | 
|  | Input_file(const Input_file_argument* input_argument) | 
|  | : input_argument_(input_argument), found_name_(), file_(), | 
|  | is_in_sysroot_(false), format_(FORMAT_NONE) | 
|  | { } | 
|  |  | 
|  | // Create an input file given just a filename. | 
|  | Input_file(const char* name); | 
|  |  | 
|  | // Create an input file with the contents already provided.  This is | 
|  | // only used for testing.  With this path, don't call the open | 
|  | // method. | 
|  | Input_file(const Task*, const char* name, const unsigned char* contents, | 
|  | off_t size); | 
|  |  | 
|  | // Return the command line argument. | 
|  | const Input_file_argument* | 
|  | input_file_argument() const | 
|  | { return this->input_argument_; } | 
|  |  | 
|  | // Return whether this is a file that we will search for in the list | 
|  | // of directories. | 
|  | bool | 
|  | will_search_for() const; | 
|  |  | 
|  | // Open the file.  If the open fails, this will report an error and | 
|  | // return false.  If there is a search, it starts at directory | 
|  | // *PINDEX.  *PINDEX should be initialized to zero.  It may be | 
|  | // restarted to find the next file with a matching name by | 
|  | // incrementing the result and calling this again. | 
|  | bool | 
|  | open(const Dirsearch&, const Task*, int* pindex); | 
|  |  | 
|  | // Return the name given by the user.  For -lc this will return "c". | 
|  | const char* | 
|  | name() const; | 
|  |  | 
|  | // Return the file name.  For -lc this will return something like | 
|  | // "/usr/lib/libc.so". | 
|  | const std::string& | 
|  | filename() const | 
|  | { return this->file_.filename(); } | 
|  |  | 
|  | // Return the name under which we found the file, corresponding to | 
|  | // the command line.  For -lc this will return something like | 
|  | // "libc.so". | 
|  | const std::string& | 
|  | found_name() const | 
|  | { return this->found_name_; } | 
|  |  | 
|  | // Return the position dependent options. | 
|  | const Position_dependent_options& | 
|  | options() const; | 
|  |  | 
|  | // Return the file. | 
|  | File_read& | 
|  | file() | 
|  | { return this->file_; } | 
|  |  | 
|  | const File_read& | 
|  | file() const | 
|  | { return this->file_; } | 
|  |  | 
|  | // Whether we found the file in a directory in the system root. | 
|  | bool | 
|  | is_in_sysroot() const | 
|  | { return this->is_in_sysroot_; } | 
|  |  | 
|  | // Whether this file is in a system directory. | 
|  | bool | 
|  | is_in_system_directory() const; | 
|  |  | 
|  | // Return whether this file is to be read only for its symbols. | 
|  | bool | 
|  | just_symbols() const; | 
|  |  | 
|  | // Return the format of the unconverted input file. | 
|  | Format | 
|  | format() const | 
|  | { return this->format_; } | 
|  |  | 
|  | // Try to find a file in the extra search dirs.  Returns true on success. | 
|  | static bool | 
|  | try_extra_search_path(int* pindex, | 
|  | const Input_file_argument* input_argument, | 
|  | std::string filename, std::string* found_name, | 
|  | std::string* namep); | 
|  |  | 
|  | // Find the actual file. | 
|  | static bool | 
|  | find_file(const Dirsearch& dirpath, int* pindex, | 
|  | const Input_file_argument* input_argument, | 
|  | bool* is_in_sysroot, | 
|  | std::string* found_name, std::string* namep); | 
|  |  | 
|  | private: | 
|  | Input_file(const Input_file&); | 
|  | Input_file& operator=(const Input_file&); | 
|  |  | 
|  | // Open a binary file. | 
|  | bool | 
|  | open_binary(const Task* task, const std::string& name); | 
|  |  | 
|  | // The argument from the command line. | 
|  | const Input_file_argument* input_argument_; | 
|  | // The name under which we opened the file.  This is like the name | 
|  | // on the command line, but -lc turns into libc.so (or whatever). | 
|  | // It only includes the full path if the path was on the command | 
|  | // line. | 
|  | std::string found_name_; | 
|  | // The file after we open it. | 
|  | File_read file_; | 
|  | // Whether we found the file in a directory in the system root. | 
|  | bool is_in_sysroot_; | 
|  | // Format of unconverted input file. | 
|  | Format format_; | 
|  | }; | 
|  |  | 
|  | } // end namespace gold | 
|  |  | 
|  | #endif // !defined(GOLD_FILEREAD_H) |