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, &section_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