1// 2// Copyright 2012-2016 Francisco Jerez 3// Copyright 2012-2016 Advanced Micro Devices, Inc. 4// 5// Permission is hereby granted, free of charge, to any person obtaining a 6// copy of this software and associated documentation files (the "Software"), 7// to deal in the Software without restriction, including without limitation 8// the rights to use, copy, modify, merge, publish, distribute, sublicense, 9// and/or sell copies of the Software, and to permit persons to whom the 10// Software is furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21// OTHER DEALINGS IN THE SOFTWARE. 22// 23 24/// 25/// \file 26/// Generate code using an arbitrary LLVM back-end capable of emitting 27/// executable code as an ELF object file. 28/// 29 30#include <llvm/Target/TargetMachine.h> 31#include <llvm/Transforms/Utils/Cloning.h> 32 33#include "llvm/codegen.hpp" 34#include "llvm/compat.hpp" 35#include "llvm/util.hpp" 36#include "core/error.hpp" 37 38using clover::binary; 39using clover::build_error; 40using namespace clover::llvm; 41using ::llvm::TargetMachine; 42 43#ifdef HAVE_CLOVER_NATIVE 44 45#include <libelf.h> 46#include <gelf.h> 47 48namespace { 49 namespace elf { 50 std::unique_ptr<Elf, int (*)(Elf *)> 51 get(const std::vector<char> &code) { 52 // One of the libelf implementations 53 // (http://www.mr511.de/software/english.htm) requires calling 54 // elf_version() before elf_memory(). 55 elf_version(EV_CURRENT); 56 return { elf_memory(const_cast<char *>(code.data()), code.size()), 57 elf_end }; 58 } 59 60 Elf_Scn * 61 get_symbol_table(Elf *elf) { 62 size_t section_str_index; 63 elf_getshdrstrndx(elf, §ion_str_index); 64 65 for (Elf_Scn *s = elf_nextscn(elf, NULL); s; s = elf_nextscn(elf, s)) { 66 GElf_Shdr header; 67 if (gelf_getshdr(s, &header) != &header) 68 return nullptr; 69 70 if (!std::strcmp(elf_strptr(elf, section_str_index, header.sh_name), 71 ".symtab")) 72 return s; 73 } 74 75 return nullptr; 76 } 77 78 std::map<std::string, unsigned> 79 get_symbol_offsets(Elf *elf, Elf_Scn *symtab) { 80 Elf_Data *const symtab_data = elf_getdata(symtab, NULL); 81 GElf_Shdr header; 82 if (gelf_getshdr(symtab, &header) != &header) 83 return {}; 84 85 std::map<std::string, unsigned> symbol_offsets; 86 GElf_Sym symbol; 87 unsigned i = 0; 88 89 while (GElf_Sym *s = gelf_getsym(symtab_data, i++, &symbol)) { 90 const char *name = elf_strptr(elf, header.sh_link, s->st_name); 91 symbol_offsets[name] = s->st_value; 92 } 93 94 return symbol_offsets; 95 } 96 } 97 98 std::map<std::string, unsigned> 99 get_symbol_offsets(const std::vector<char> &code, std::string &r_log) { 100 const auto elf = elf::get(code); 101 const auto symtab = elf::get_symbol_table(elf.get()); 102 if (!symtab) 103 fail(r_log, build_error(), "Unable to find symbol table."); 104 105 return elf::get_symbol_offsets(elf.get(), symtab); 106 } 107 108 std::vector<char> 109 emit_code(::llvm::Module &mod, const target &target, 110 compat::CodeGenFileType ft, 111 std::string &r_log) { 112 std::string err; 113 auto t = ::llvm::TargetRegistry::lookupTarget(target.triple, err); 114 if (!t) 115 fail(r_log, build_error(), err); 116 117 std::unique_ptr<TargetMachine> tm { 118 t->createTargetMachine(target.triple, target.cpu, "", {}, 119 ::llvm::None, ::llvm::None, 120 ::llvm::CodeGenOpt::Default) }; 121 if (!tm) 122 fail(r_log, build_error(), 123 "Could not create TargetMachine: " + target.triple); 124 125 ::llvm::SmallVector<char, 1024> data; 126 127 { 128 ::llvm::legacy::PassManager pm; 129 ::llvm::raw_svector_ostream os { data }; 130 131 mod.setDataLayout(tm->createDataLayout()); 132 tm->Options.MCOptions.AsmVerbose = 133 (ft == compat::CGFT_AssemblyFile); 134 135 if (tm->addPassesToEmitFile(pm, os, nullptr, ft)) 136 fail(r_log, build_error(), "TargetMachine can't emit this file"); 137 138 pm.run(mod); 139 } 140 141 return { data.begin(), data.end() }; 142 } 143} 144 145binary 146clover::llvm::build_module_native(::llvm::Module &mod, const target &target, 147 const clang::CompilerInstance &c, 148 std::string &r_log) { 149 const auto code = emit_code(mod, target, 150 compat::CGFT_ObjectFile, r_log); 151 return build_module_common(mod, code, get_symbol_offsets(code, r_log), c); 152} 153 154std::string 155clover::llvm::print_module_native(const ::llvm::Module &mod, 156 const target &target) { 157 std::string log; 158 try { 159 std::unique_ptr< ::llvm::Module> cmod { ::llvm::CloneModule(mod) }; 160 return as_string(emit_code(*cmod, target, 161 compat::CGFT_AssemblyFile, log)); 162 } catch (...) { 163 return "Couldn't output native disassembly: " + log; 164 } 165} 166 167#else 168 169binary 170clover::llvm::build_module_native(::llvm::Module &mod, const target &target, 171 const clang::CompilerInstance &c, 172 std::string &r_log) { 173 unreachable("Native codegen support disabled at build time"); 174} 175 176std::string 177clover::llvm::print_module_native(const ::llvm::Module &mod, 178 const target &target) { 179 unreachable("Native codegen support disabled at build time"); 180} 181 182#endif 183