// Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #include "rustllvm.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" using namespace llvm; using namespace llvm::object; struct RustArchiveMember { const char *filename; const char *name; Archive::Child child; RustArchiveMember(): filename(NULL), name(NULL), #if LLVM_VERSION_MINOR >= 8 child(NULL, NULL, NULL) #else child(NULL, NULL) #endif {} ~RustArchiveMember() {} }; struct RustArchiveIterator { Archive::child_iterator cur; Archive::child_iterator end; #if LLVM_VERSION_MINOR >= 9 Error err; #endif }; enum class LLVMRustArchiveKind { Other, GNU, MIPS64, BSD, COFF, }; static Archive::Kind from_rust(LLVMRustArchiveKind kind) { switch (kind) { case LLVMRustArchiveKind::GNU: return Archive::K_GNU; case LLVMRustArchiveKind::MIPS64: return Archive::K_MIPS64; case LLVMRustArchiveKind::BSD: return Archive::K_BSD; case LLVMRustArchiveKind::COFF: return Archive::K_COFF; default: llvm_unreachable("Bad ArchiveKind."); } } typedef OwningBinary *LLVMRustArchiveRef; typedef RustArchiveMember *LLVMRustArchiveMemberRef; typedef Archive::Child *LLVMRustArchiveChildRef; typedef Archive::Child const *LLVMRustArchiveChildConstRef; typedef RustArchiveIterator *LLVMRustArchiveIteratorRef; extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *path) { ErrorOr> buf_or = MemoryBuffer::getFile(path, -1, false); if (!buf_or) { LLVMRustSetLastError(buf_or.getError().message().c_str()); return nullptr; } #if LLVM_VERSION_MINOR <= 8 ErrorOr> archive_or = #else Expected> archive_or = #endif Archive::create(buf_or.get()->getMemBufferRef()); if (!archive_or) { #if LLVM_VERSION_MINOR <= 8 LLVMRustSetLastError(archive_or.getError().message().c_str()); #else LLVMRustSetLastError(toString(archive_or.takeError()).c_str()); #endif return nullptr; } OwningBinary *ret = new OwningBinary( std::move(archive_or.get()), std::move(buf_or.get())); return ret; } extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef ar) { delete ar; } extern "C" LLVMRustArchiveIteratorRef LLVMRustArchiveIteratorNew(LLVMRustArchiveRef ra) { Archive *ar = ra->getBinary(); RustArchiveIterator *rai = new RustArchiveIterator(); #if LLVM_VERSION_MINOR <= 8 rai->cur = ar->child_begin(); #else rai->cur = ar->child_begin(rai->err); if (rai->err) { LLVMRustSetLastError(toString(std::move(rai->err)).c_str()); return NULL; } #endif rai->end = ar->child_end(); return rai; } extern "C" LLVMRustArchiveChildConstRef LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef rai) { #if LLVM_VERSION_MINOR >= 9 if (rai->err) { LLVMRustSetLastError(toString(std::move(rai->err)).c_str()); return NULL; } #endif if (rai->cur == rai->end) return NULL; #if LLVM_VERSION_MINOR == 8 const ErrorOr* cur = rai->cur.operator->(); if (!*cur) { LLVMRustSetLastError(cur->getError().message().c_str()); return NULL; } const Archive::Child &child = cur->get(); #else const Archive::Child &child = *rai->cur.operator->(); #endif Archive::Child *ret = new Archive::Child(child); ++rai->cur; return ret; } extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef child) { delete child; } extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef rai) { delete rai; } extern "C" const char* LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef child, size_t *size) { ErrorOr name_or_err = child->getName(); if (name_or_err.getError()) return NULL; StringRef name = name_or_err.get(); *size = name.size(); return name.data(); } extern "C" const char* LLVMRustArchiveChildData(LLVMRustArchiveChildRef child, size_t *size) { StringRef buf; ErrorOr buf_or_err = child->getBuffer(); if (buf_or_err.getError()) { LLVMRustSetLastError(buf_or_err.getError().message().c_str()); return NULL; } buf = buf_or_err.get(); *size = buf.size(); return buf.data(); } extern "C" LLVMRustArchiveMemberRef LLVMRustArchiveMemberNew(char *Filename, char *Name, LLVMRustArchiveChildRef child) { RustArchiveMember *Member = new RustArchiveMember; Member->filename = Filename; Member->name = Name; if (child) Member->child = *child; return Member; } extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) { delete Member; } extern "C" LLVMRustResult LLVMRustWriteArchive(char *Dst, size_t NumMembers, const LLVMRustArchiveMemberRef *NewMembers, bool WriteSymbtab, LLVMRustArchiveKind rust_kind) { #if LLVM_VERSION_MINOR <= 8 std::vector Members; #else std::vector Members; #endif auto Kind = from_rust(rust_kind); for (size_t i = 0; i < NumMembers; i++) { auto Member = NewMembers[i]; assert(Member->name); if (Member->filename) { #if LLVM_VERSION_MINOR >= 9 Expected MOrErr = NewArchiveMember::getFile(Member->filename, true); if (!MOrErr) { LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); return LLVMRustResult::Failure; } Members.push_back(std::move(*MOrErr)); #elif LLVM_VERSION_MINOR == 8 Members.push_back(NewArchiveIterator(Member->filename)); #else Members.push_back(NewArchiveIterator(Member->filename, Member->name)); #endif } else { #if LLVM_VERSION_MINOR <= 8 Members.push_back(NewArchiveIterator(Member->child, Member->name)); #else Expected MOrErr = NewArchiveMember::getOldMember(Member->child, true); if (!MOrErr) { LLVMRustSetLastError(toString(MOrErr.takeError()).c_str()); return LLVMRustResult::Failure; } Members.push_back(std::move(*MOrErr)); #endif } } #if LLVM_VERSION_MINOR >= 8 auto pair = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false); #else auto pair = writeArchive(Dst, Members, WriteSymbtab, Kind, true); #endif if (!pair.second) return LLVMRustResult::Success; LLVMRustSetLastError(pair.second.message().c_str()); return LLVMRustResult::Failure; }