blob: ba880bf425e1e0faf8289f6a6bd1e87d01ebc861 [file] [log] [blame]
//===-- 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);
}