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/codegen.hpp"
31#include "llvm/compat.hpp"
32#include "llvm/util.hpp"
33#include "core/error.hpp"
34
35#include <llvm/Target/TargetMachine.h>
36#include <llvm/Support/TargetRegistry.h>
37#include <llvm/Transforms/Utils/Cloning.h>
38
39#include <libelf.h>
40#include <gelf.h>
41
42using namespace clover;
43using namespace clover::llvm;
44using ::llvm::TargetMachine;
45
46namespace {
47   namespace elf {
48      std::unique_ptr<Elf, int (*)(Elf *)>
49      get(const std::vector<char> &code) {
50         // One of the libelf implementations
51         // (http://www.mr511.de/software/english.htm) requires calling
52         // elf_version() before elf_memory().
53         elf_version(EV_CURRENT);
54         return { elf_memory(const_cast<char *>(code.data()), code.size()),
55                  elf_end };
56      }
57
58      Elf_Scn *
59      get_symbol_table(Elf *elf) {
60         size_t section_str_index;
61         elf_getshdrstrndx(elf, &section_str_index);
62
63         for (Elf_Scn *s = elf_nextscn(elf, NULL); s; s = elf_nextscn(elf, s)) {
64            GElf_Shdr header;
65            if (gelf_getshdr(s, &header) != &header)
66               return nullptr;
67
68            if (!std::strcmp(elf_strptr(elf, section_str_index, header.sh_name),
69                             ".symtab"))
70               return s;
71         }
72
73         return nullptr;
74      }
75
76      std::map<std::string, unsigned>
77      get_symbol_offsets(Elf *elf, Elf_Scn *symtab) {
78         Elf_Data *const symtab_data = elf_getdata(symtab, NULL);
79         GElf_Shdr header;
80         if (gelf_getshdr(symtab, &header) != &header)
81            return {};
82
83         std::map<std::string, unsigned> symbol_offsets;
84         GElf_Sym symbol;
85         unsigned i = 0;
86
87         while (GElf_Sym *s = gelf_getsym(symtab_data, i++, &symbol)) {
88            const char *name = elf_strptr(elf, header.sh_link, s->st_name);
89            symbol_offsets[name] = s->st_value;
90         }
91
92         return symbol_offsets;
93      }
94   }
95
96   std::map<std::string, unsigned>
97   get_symbol_offsets(const std::vector<char> &code, std::string &r_log) {
98      const auto elf = elf::get(code);
99      const auto symtab = elf::get_symbol_table(elf.get());
100      if (!symtab)
101         fail(r_log, build_error(), "Unable to find symbol table.");
102
103      return elf::get_symbol_offsets(elf.get(), symtab);
104   }
105
106   std::vector<char>
107   emit_code(::llvm::Module &mod, const target &target,
108             TargetMachine::CodeGenFileType ft,
109             std::string &r_log) {
110      std::string err;
111      auto t = ::llvm::TargetRegistry::lookupTarget(target.triple, err);
112      if (!t)
113         fail(r_log, build_error(), err);
114
115      std::unique_ptr<TargetMachine> tm {
116         t->createTargetMachine(target.triple, target.cpu, "", {},
117                                ::llvm::None, compat::default_code_model,
118                                ::llvm::CodeGenOpt::Default) };
119      if (!tm)
120         fail(r_log, build_error(),
121              "Could not create TargetMachine: " + target.triple);
122
123      ::llvm::SmallVector<char, 1024> data;
124
125      {
126         ::llvm::legacy::PassManager pm;
127         ::llvm::raw_svector_ostream os { data };
128
129         mod.setDataLayout(tm->createDataLayout());
130         tm->Options.MCOptions.AsmVerbose =
131            (ft == TargetMachine::CGFT_AssemblyFile);
132
133         if (compat::add_passes_to_emit_file(*tm, pm, os, ft))
134            fail(r_log, build_error(), "TargetMachine can't emit this file");
135
136         pm.run(mod);
137      }
138
139      return { data.begin(), data.end() };
140   }
141}
142
143module
144clover::llvm::build_module_native(::llvm::Module &mod, const target &target,
145                                  const clang::CompilerInstance &c,
146                                  std::string &r_log) {
147   const auto code = emit_code(mod, target,
148                               TargetMachine::CGFT_ObjectFile, r_log);
149   return build_module_common(mod, code, get_symbol_offsets(code, r_log), c);
150}
151
152std::string
153clover::llvm::print_module_native(const ::llvm::Module &mod,
154                                  const target &target) {
155   std::string log;
156   try {
157      std::unique_ptr< ::llvm::Module> cmod { compat::clone_module(mod) };
158      return as_string(emit_code(*cmod, target,
159                                 TargetMachine::CGFT_AssemblyFile, log));
160   } catch (...) {
161      return "Couldn't output native disassembly: " + log;
162   }
163}
164