| //===-- go-backend.cpp - backend specific go utility routines -------------===// |
| // |
| // Copyright 2018 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. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Backend-specific helper routines invoked by the go frontend. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm-includes.h" |
| #include <ctype.h> |
| #include <iostream> |
| |
| #include "go-llvm-diagnostics.h" |
| #include "go-c.h" |
| |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Object/Archive.h" |
| #include "llvm/Object/Binary.h" |
| #include "llvm/Object/ObjectFile.h" |
| |
| // Size of archive member header in bytes |
| #define ARCHIVE_MEMBER_HEADER_SIZE 60 |
| |
| #ifndef GO_EXPORT_SEGMENT_NAME |
| #define GO_EXPORT_SEGMENT_NAME "__GNU_GO" |
| #endif |
| |
| /* The section name we use when reading and writing export data. */ |
| |
| #ifndef GO_EXPORT_SECTION_NAME |
| #define GO_EXPORT_SECTION_NAME ".go_export" |
| #endif |
| |
| /* Return whether or not we've reported any errors. */ |
| |
| bool |
| saw_errors (void) |
| { |
| return go_be_saw_errors(); |
| } |
| |
| // Called by the Go frontend proper if the unsafe package was imported. |
| // |
| // FIXME: make a determination about whether we need to run TBAA |
| // in a different way based on this information. |
| |
| void |
| go_imported_unsafe (void) |
| { |
| } |
| |
| static const char * |
| readExportDataFromObject(llvm::object::ObjectFile *obj, |
| int *perr, |
| char **pbuf, |
| size_t *plen) |
| { |
| // Walk sections |
| for (llvm::object::section_iterator si = obj->section_begin(), |
| se = obj->section_end(); si != se; ++si) { |
| llvm::object::SectionRef sref = *si; |
| llvm::Expected<llvm::StringRef> sname = sref.getName(); |
| if (! sname) |
| break; |
| if (*sname == GO_EXPORT_SECTION_NAME) { |
| // Extract section of interest |
| llvm::Expected<llvm::StringRef> bytes = sref.getContents(); |
| if (! bytes) { |
| consumeError(bytes.takeError()); |
| *perr = errno; |
| return "get section contents"; |
| } |
| char *buf = new char[bytes->size()]; |
| if (! buf) { |
| *perr = errno; |
| return "malloc"; |
| } |
| memcpy(buf, bytes->data(), bytes->size()); |
| *pbuf = buf; |
| *plen = bytes->size(); |
| return nullptr; |
| } |
| } |
| return nullptr; |
| } |
| |
| static const char * |
| readExportDataFromArchive(llvm::object::Archive *archive, |
| off_t offset, |
| int *perr, |
| char **pbuf, |
| size_t *plen) |
| { |
| llvm::Error err = llvm::Error::success(); |
| |
| // The gofrontend archive reader passes in an offset that points |
| // past the the archive member header, whereas the llvm::object::Archive |
| // code considers "child offset" to be the start of the region in the |
| // archive at the point of the member header. Adjust accordingly. |
| assert(!offset || offset > ARCHIVE_MEMBER_HEADER_SIZE); |
| uint64_t uoffset = static_cast<uint64_t>(offset) - |
| ARCHIVE_MEMBER_HEADER_SIZE; |
| for (auto &child : archive->children(err)) { |
| if (err) |
| return "unable to open as archive"; |
| if (child.getChildOffset() != uoffset) |
| continue; |
| // found. |
| llvm::Expected<std::unique_ptr<llvm::object::Binary>> childOrErr = |
| child.getAsBinary(); |
| if (auto err = childOrErr.takeError()) { |
| // Ignore error if this is not recognized as an object file. |
| // This is also what gccgo does (go-backend.c:go_read_export_data). |
| // In particular, cgo archive may contain _cgo_flags as a member, |
| // which is not an object file. |
| consumeError(std::move(err)); |
| return nullptr; |
| } |
| llvm::object::ObjectFile *o = |
| llvm::dyn_cast<llvm::object::ObjectFile>(&*childOrErr.get()); |
| if (o) |
| return readExportDataFromObject(o, perr, pbuf, plen); |
| } |
| |
| if (err) |
| return "unable to open as archive"; |
| else |
| return nullptr; |
| } |
| |
| /* The go_read_export_data function is called by the Go frontend |
| proper to read Go export data from an object file. FD is a file |
| descriptor open for reading. OFFSET is the offset within the file |
| where the object file starts; this will be 0 except when reading an |
| archive. On success this returns NULL and sets *PBUF to a buffer |
| allocated using malloc, of size *PLEN, holding the export data. If |
| the data is not found, this returns NULL and sets *PBUF to NULL and |
| *PLEN to 0. If some error occurs, this returns an error message |
| and sets *PERR to an errno value or 0 if there is no relevant |
| errno. */ |
| |
| const char * |
| go_read_export_data (int fd, off_t offset, char **pbuf, size_t *plen, |
| int *perr) |
| { |
| *pbuf = NULL; |
| *plen = 0; |
| |
| // Create memory buffer for this file descriptor |
| auto BuffOrErr = llvm::MemoryBuffer::getOpenFile(fd, "", -1); |
| if (! BuffOrErr) |
| return nullptr; // ignore this error |
| std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BuffOrErr.get()); |
| |
| // Examine buffer as binary |
| llvm::Expected<std::unique_ptr<llvm::object::Binary>> BinOrErr = |
| llvm::object::createBinary(Buffer->getMemBufferRef()); |
| if (!BinOrErr) |
| return nullptr; // also ignore here |
| std::unique_ptr<llvm::object::Binary> Binary = std::move(BinOrErr.get()); |
| |
| // Examine binary as archive |
| if (llvm::object::Archive *a = |
| llvm::dyn_cast<llvm::object::Archive>(Binary.get())) |
| return readExportDataFromArchive(a, offset, perr, pbuf, plen); |
| |
| // Examine binary as object |
| llvm::object::ObjectFile *o = |
| llvm::dyn_cast<llvm::object::ObjectFile>(Binary.get()); |
| if (o) |
| return readExportDataFromObject(o, perr, pbuf, plen); |
| |
| return nullptr; |
| } |
| |
| const char *lbasename(const char *path) |
| { |
| // TODO: add windows support |
| const char *cur, *rval = path; |
| |
| for (cur = path; *cur; cur++) |
| if (*cur == '/') |
| rval = cur + 1; |
| |
| return rval; |
| } |
| |
| const char *xstrerror(int e) |
| { |
| static char unknown_ebuf[128]; |
| const char *se = strerror(e); |
| if (se) |
| return se; |
| sprintf(unknown_ebuf, "unknown error #%d", e); |
| se = unknown_ebuf; |
| return se; |
| } |
| |
| bool IS_DIR_SEPARATOR(char c) |
| { |
| // TODO: windows support |
| return c == '/'; |
| } |
| |
| extern bool ISXDIGIT(char c) |
| { |
| // TODO: update if better locale support added |
| return isxdigit(c); |
| } |