From patchwork Tue Nov 2 22:59:42 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Lance Taylor X-Patchwork-Id: 69929 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 9EBF21007D4 for ; Wed, 3 Nov 2010 10:00:10 +1100 (EST) Received: (qmail 30525 invoked by alias); 2 Nov 2010 23:00:01 -0000 Received: (qmail 30514 invoked by uid 22791); 2 Nov 2010 22:59:57 -0000 X-SWARE-Spam-Status: No, hits=-4.6 required=5.0 tests=AWL, BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_HI, SPF_HELO_PASS, TW_CC, T_FILL_THIS_FORM_SHORT, T_RP_MATCHES_RCVD, T_TVD_MIME_NO_HEADERS X-Spam-Check-By: sourceware.org Received: from smtp-out.google.com (HELO smtp-out.google.com) (74.125.121.35) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 02 Nov 2010 22:59:50 +0000 Received: from wpaz5.hot.corp.google.com (wpaz5.hot.corp.google.com [172.24.198.69]) by smtp-out.google.com with ESMTP id oA2MxlnM002004 for ; Tue, 2 Nov 2010 15:59:47 -0700 Received: from pvb32 (pvb32.prod.google.com [10.241.209.96]) by wpaz5.hot.corp.google.com with ESMTP id oA2Mxcoh004466 for ; Tue, 2 Nov 2010 15:59:46 -0700 Received: by pvb32 with SMTP id 32so1104243pvb.33 for ; Tue, 02 Nov 2010 15:59:45 -0700 (PDT) Received: by 10.142.157.6 with SMTP id f6mr6444200wfe.248.1288738785770; Tue, 02 Nov 2010 15:59:45 -0700 (PDT) Received: from coign.google.com (dhcp-172-22-123-203.mtv.corp.google.com [172.22.123.203]) by mx.google.com with ESMTPS id v19sm11858513wfh.12.2010.11.02.15.59.43 (version=TLSv1/SSLv3 cipher=RC4-MD5); Tue, 02 Nov 2010 15:59:44 -0700 (PDT) From: Ian Lance Taylor To: gcc-patches@gcc.gnu.org, gofrontend-dev@googlegroups.com Subject: [gccgo] Use simple-object instead of elfpp Date: Tue, 02 Nov 2010 15:59:42 -0700 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (gnu/linux) MIME-Version: 1.0 X-System-Of-Record: true X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org This patch changes gccgo to use the new simple-object interface instead of elfcpp. This lets it work with PE/COFF and Mach-O as well as ELF. Full support for Mach-O will also requiring adding a segment name when generating the .go_export section. Committed to gccgo branch. Ian diff -r d84d3a63e9ca go/Make-lang.in --- a/go/Make-lang.in Sun Oct 31 11:15:12 2010 -0700 +++ b/go/Make-lang.in Tue Nov 02 15:56:27 2010 -0700 @@ -58,7 +58,6 @@ go/gogo.o \ go/import.o \ go/import-archive.o \ - go/import-elf.o \ go/lex.o \ go/parse.o \ go/statements.o \ @@ -165,10 +164,9 @@ $(GO_STATEMENTS_H) $(GO_EXPRESSIONS_H) go/dataflow.h $(GO_IMPORT_H) \ go/export.h $(GO_GOGO_H) go/import.o: go/import.cc $(GO_SYSTEM_H) $(srcdir)/../include/filenames.h \ - $(GO_C_H) $(GO_GOGO_H) $(GO_TYPES_H) go/export.h $(GO_IMPORT_H) + ../include/simple-object.h $(GO_C_H) $(GO_GOGO_H) $(GO_TYPES_H) \ + go/export.h $(GO_IMPORT_H) go/import-archive.o: go/import-archive.cc $(GO_SYSTEM_H) $(GO_IMPORT_H) -go/import-elf.o: go/import-elf.cc $(GO_SYSTEM_H) intl.h $(DIAGNOSTIC_H) \ - $(ELFCPP_H) $(srcdir)/../elfcpp/elfcpp_file.h $(GO_IMPORT_H) go/lex.o: go/lex.cc $(GO_LEX_H) $(GO_SYSTEM_H) go/parse.o: go/parse.cc $(GO_SYSTEM_H) $(GO_LEX_H) $(GO_GOGO_H) $(GO_TYPES_H) \ $(GO_STATEMENTS_H) $(GO_EXPRESSIONS_H) $(GO_PARSE_H) diff -r d84d3a63e9ca go/gogo.cc --- a/go/gogo.cc Sun Oct 31 11:15:12 2010 -0700 +++ b/go/gogo.cc Tue Nov 02 15:56:27 2010 -0700 @@ -320,6 +320,8 @@ Package* package = imp.import(this, local_name, is_local_name_exported); this->imports_.insert(std::make_pair(filename, package)); package->set_is_imported(); + + delete stream; } // Return whether we are at the global binding level. diff -r d84d3a63e9ca go/import-archive.cc --- a/go/import-archive.cc Sun Oct 31 11:15:12 2010 -0700 +++ b/go/import-archive.cc Tue Nov 02 15:56:27 2010 -0700 @@ -642,10 +642,10 @@ &member_fd, &member_off, &member_name)) return NULL; - Import::Stream* is = Import::find_elf_export_data(member_name, - member_fd, - member_off, - location); + Import::Stream* is = Import::find_object_export_data(member_name, + member_fd, + member_off, + location); if (is != NULL) { ret->add(is); diff -r d84d3a63e9ca go/import-elf.cc --- a/go/import-elf.cc Sun Oct 31 11:15:12 2010 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,333 +0,0 @@ -// import-elf.cc -- Go frontend read import data from an ELF file. - -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "go-system.h" - -#include -#include -#include -#include -#include -#include -#include - -extern "C" -{ -#include "intl.h" -#include "diagnostic.h" -} - -#include "elfcpp.h" -#include "elfcpp_file.h" -#include "import.h" - -// The functions in this file extract Go export data from an ELF -// object file or shared library. - -const int Import::elf_magic_len; - -// Return true if BYTES, which are from the start of the file, are an -// ELF magic number. - -bool -Import::is_elf_magic(const char* bytes) -{ - const char elfmagic[Import::elf_magic_len] = - { - elfcpp::ELFMAG0, elfcpp::ELFMAG1, elfcpp::ELFMAG2, elfcpp::ELFMAG3 - }; - return memcmp(bytes, elfmagic, Import::elf_magic_len) == 0; -} - -// The base of the template class used to read the ELF file. - -class Elf_object -{ - public: - Elf_object(const std::string& name, const unsigned char* contents, - off_t file_size, source_location location) - : name_(name), contents_(contents), file_size_(file_size), - location_(location), reported_error_(false) - { } - - virtual ~Elf_object() - { } - - // Look for a .go.export section. If found, store the section - // contents in *CONTENTS and return true. Otherwise, return false. - bool - find_export_section(std::string* contents) - { return this->do_find_export_section(contents); } - - // A view into the file. This is used by elfcpp::Elf_file. - class View - { - public: - View(const unsigned char* data) - : data_(data) - { } - - const unsigned char* - data() const - { return this->data_; } - - private: - const unsigned char* data_; - }; - - // Return a view into the file. This is called by elfcpp::Elf_file. - View - view(off_t file_offset, off_t data_size); - - // A location in the file. - class Location - { - public: - Location(off_t file_offset, off_t data_size) - : file_offset_(file_offset), data_size_(data_size) - { } - - off_t - file_offset() const - { return this->file_offset_; } - - off_t - data_size() const - { return this->data_size_; } - - private: - off_t file_offset_; - off_t data_size_; - }; - - View - view(const Location& loc); - - // Report an error and do not return. - void - error(const char* format, ...) ATTRIBUTE_GCC_DIAG(2, 3); - - protected: - // Implemented by size/endian specific child class. - virtual bool - do_find_export_section(std::string* contents) = 0; - - private: - // Report a truncation error. - void - report_error(); - - // File name. - std::string name_; - // File contents. - const unsigned char* contents_; - // File size. - off_t file_size_; - // Source location of import statement. - source_location location_; - // Whether we have reported an error about this file. - bool reported_error_; -}; - -// Report an error and do not return. - -void -Elf_object::error(const char* format, ...) -{ - va_list ap; - va_start(ap, format); - diagnostic_info diagnostic; - diagnostic_set_info(&diagnostic, format, &ap, this->location_, DK_ERROR); - // FIXME: Can't return. - sorry("can't continue"); -} - -// Report a truncation error. - -void -Elf_object::report_error() -{ - if (!this->reported_error_) - { - error_at(this->location_, "%s: truncated ELF file", this->name_.c_str()); - this->reported_error_ = true; - } -} - -// Return a view into the file. This is called by elfcpp::Elf_file. - -Elf_object::View -Elf_object::view(off_t file_offset, off_t data_size) -{ - const unsigned char* data; - if (file_offset + data_size <= this->file_size_) - data = this->contents_ + file_offset; - else - { - this->report_error(); - data = new unsigned char[data_size]; - } - return View(data); -} - -// Return a view given a location. - -Elf_object::View -Elf_object::view(const Location& loc) -{ - const unsigned char* data; - if (loc.file_offset() + loc.data_size() <= this->file_size_) - data = this->contents_ + loc.file_offset(); - else - { - this->report_error(); - data = new unsigned char[loc.data_size()]; - } - return View(data); -} - -// This version of Elf_object is specific to the size and endianness -// of the ELF file. - -template -class Sized_elf_object : public Elf_object -{ - public: - Sized_elf_object(const std::string& name, const unsigned char* contents, - off_t file_size, source_location location, - const elfcpp::Ehdr& ehdr) - : Elf_object(name, contents, file_size, location), - elf_file_(this, ehdr) - { } - - protected: - // Return the contents of the .go.export section. - bool - do_find_export_section(std::string* contents); - - private: - // The ELF file accessor. - elfcpp::Elf_file elf_file_; -}; - -// Look for a section named .go.export. If we find it, return true -// and set *CONTENTS to its contents. - -template -bool -Sized_elf_object::do_find_export_section( - std::string* contents) -{ - unsigned int shnum = this->elf_file_.shnum(); - for (unsigned int i = 0; i < shnum; ++i) - { - if (this->elf_file_.section_name(i) == ".go_export") - { - Elf_object::Location l = this->elf_file_.section_contents(i); - Elf_object::View v = this->view(l); - contents->assign(reinterpret_cast(v.data()), - l.data_size()); - return true; - } - } - return false; -} - -// This function extracts Go export data from an ELF object file. If -// it finds any, it returns an Import::Stream object for it. NAME is -// the name of the file. The object file has already been opened: FD -// is the file descriptor and FILE_SIZE is the size of the file. -// OFFSET is the offset at which to start reading from the file; this -// is to support archives. LOCATION is the location of the import -// statement. - -Import::Stream* -Import::find_elf_export_data(const std::string& name, int fd, off_t offset, - source_location location) -{ - struct stat st; - if (fstat(fd, &st) < 0) - { - error_at(location, "%s: %s", name.c_str(), strerror(errno)); - return NULL; - } - const off_t file_size = st.st_size; - - // For now we assume that the host system support mmap. This could - // be changed to support reading the bytes directly. FIXME. - void* map = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (map == MAP_FAILED) - { - error_at(location, "%s: mmap failed: %s", name.c_str(), - strerror(errno)); - return NULL; - } - - const unsigned char* contents = static_cast(map); - contents += offset; - - int c = contents[elfcpp::EI_CLASS]; - if (c != elfcpp::ELFCLASS32 && c != elfcpp::ELFCLASS64) - { - error_at(location, "%s: unsupported ELF class %d", name.c_str(), c); - munmap(map, file_size); - return NULL; - } - - int d = contents[elfcpp::EI_DATA]; - if (d != elfcpp::ELFDATA2LSB && d != elfcpp::ELFDATA2MSB) - { - error_at(location, "%s: unsupported ELF data encoding %d", - name.c_str(), d); - munmap(map, file_size); - return NULL; - } - - bool is_big_endian = d == elfcpp::ELFDATA2MSB; - - Elf_object* object; - if (c == elfcpp::ELFCLASS32) - { - if (is_big_endian) - { - elfcpp::Ehdr<32, true> ehdr(contents); - object = new Sized_elf_object<32, true>(name, contents, file_size, - location, ehdr); - } - else - { - elfcpp::Ehdr<32, false> ehdr(contents); - object = new Sized_elf_object<32, false>(name, contents, file_size, - location, ehdr); - } - } - else - { - if (is_big_endian) - { - elfcpp::Ehdr<64, true> ehdr(contents); - object = new Sized_elf_object<64, true>(name, contents, file_size, - location, ehdr); - } - else - { - elfcpp::Ehdr<64, false> ehdr(contents); - object = new Sized_elf_object<64, false>(name, contents, file_size, - location, ehdr); - } - } - - std::string export_data; - bool ret = object->find_export_section(&export_data); - - delete object; - munmap(map, file_size); - - if (ret) - return new Stream_from_string(export_data); - else - return NULL; -} diff -r d84d3a63e9ca go/import.cc --- a/go/import.cc Sun Oct 31 11:15:12 2010 -0700 +++ b/go/import.cc Tue Nov 02 15:56:27 2010 -0700 @@ -15,6 +15,8 @@ #include "filenames.h" } +#include "simple-object.h" + #include "go-c.h" #include "gogo.h" #include "types.h" @@ -176,9 +178,13 @@ Import::find_export_data(const std::string& filename, int fd, source_location location) { - const int len = MAX(Export::v1_magic_len, - MAX(Import::elf_magic_len, - Import::archive_magic_len)); + // See if we can read this as an object file. + Import::Stream* stream = Import::find_object_export_data(filename, fd, 0, + location); + if (stream != NULL) + return stream; + + const int len = MAX(Export::v1_magic_len, Import::archive_magic_len); char buf[len]; ssize_t c = read(fd, buf, len); @@ -189,16 +195,63 @@ if (memcmp(buf, Export::v1_magic, Export::v1_magic_len) == 0) return new Stream_from_file(fd); - // Check for export data in a section of an ELF file. - if (Import::is_elf_magic(buf)) - return Import::find_elf_export_data(filename, fd, 0, location); - + // See if we can read this as an archive. if (Import::is_archive_magic(buf)) return Import::find_archive_export_data(filename, fd, location); return NULL; } +// Look for export data in a simple_object. + +Import::Stream* +Import::find_object_export_data(const std::string& filename, + int fd, + off_t offset, + source_location location) +{ + const char* errmsg; + int err; + simple_object_read* sobj = simple_object_start_read(fd, offset, + "__GNU_GO", + &errmsg, &err); + if (sobj == NULL) + return NULL; + + off_t sec_offset; + off_t sec_length; + int found = simple_object_find_section(sobj, ".go_export", &sec_offset, + &sec_length, &errmsg, &err); + + simple_object_release_read(sobj); + + if (!found) + return NULL; + + if (lseek(fd, offset + sec_offset, SEEK_SET) < 0) + { + error_at(location, "lseek %s failed: %s", filename.c_str(), + strerror(errno)); + return NULL; + } + + char* buf = new char[sec_length]; + ssize_t c = read(fd, buf, sec_length); + if (c < 0) + { + error_at(location, "read %s failed: %s", filename.c_str(), + strerror(errno)); + return NULL; + } + if (c < sec_length) + { + error_at(location, "%s: short read", filename.c_str()); + return NULL; + } + + return new Stream_from_buffer(buf, sec_length); +} + // Class Import. // Construct an Import object. We make the builtin_types_ vector diff -r d84d3a63e9ca go/import.h --- a/go/import.h Sun Oct 31 11:15:12 2010 -0700 +++ b/go/import.h Tue Nov 02 15:56:27 2010 -0700 @@ -200,14 +200,9 @@ static Stream* find_export_data(const std::string& filename, int fd, source_location); - static const int elf_magic_len = 4; - - static bool - is_elf_magic(const char*); - static Stream* - find_elf_export_data(const std::string& filename, int fd, off_t offset, - source_location); + find_object_export_data(const std::string& filename, int fd, + off_t offset, source_location); static const int archive_magic_len = 8; @@ -289,6 +284,41 @@ size_t pos_; }; +// Read import data from an allocated buffer. + +class Stream_from_buffer : public Import::Stream +{ + public: + Stream_from_buffer(char* buf, size_t length) + : buf_(buf), length_(length), pos_(0) + { } + + ~Stream_from_buffer() + { delete[] this->buf_; } + + protected: + bool + do_peek(size_t length, const char** bytes) + { + if (this->pos_ + length > this->length_) + return false; + *bytes = this->buf_ + this->pos_; + return true; + } + + void + do_advance(size_t len) + { this->pos_ += len; } + + private: + // The data we are reading. + char* buf_; + // The length of the buffer. + size_t length_; + // The current position within the buffer. + size_t pos_; +}; + // Read import data from an open file descriptor. class Stream_from_file : public Import::Stream