TPDE
Loading...
Searching...
No Matches
Assembler.hpp
1// SPDX-FileCopyrightText: 2025 Contributors to TPDE <https://tpde.org>
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4#pragma once
5
6#include "tpde/base.hpp"
7#include "tpde/util/BumpAllocator.hpp"
8#include "tpde/util/SmallVector.hpp"
9#include <cstring>
10#include <span>
11#include <vector>
12
13namespace tpde {
14
15struct SymRef {
16private:
17 u32 val;
18
19public:
20 /// Invalid symbol reference
21 constexpr SymRef() : val(0) {}
22
23 explicit constexpr SymRef(u32 id) : val(id) {}
24
25 u32 id() const { return val; }
26 bool valid() const { return val != 0; }
27
28 bool operator==(const SymRef &other) const { return other.val == val; }
29};
30
31struct SecRef {
32private:
33 u32 val;
34
35public:
36 /// Invalid symbol reference
37 constexpr SecRef() : val(0) {}
38
39 explicit constexpr SecRef(u32 id) : val(id) {}
40
41 u32 id() const { return val; }
42 bool valid() const { return val != 0; }
43
44 bool operator==(const SecRef &other) const { return other.val == val; }
45};
46
47struct Relocation {
48 u32 offset; ///< Offset inside section.
49 SymRef symbol; ///< References symbol.
50 u32 type; ///< Relocation type. File-format-specifc.
51 i32 addend; ///< Addend.
52};
53
54struct DataSection {
55 friend class Assembler;
56 friend class AssemblerElf;
57
58 /// 256 bytes inline storage is enough for 10 relocations, which is a typical
59 /// number for a single function (relevant for COMDAT sections with one
60 /// section per function).
61 using StorageTy = util::SmallVector<u8, 256>;
62
63 /// Section data.
64 StorageTy data;
65
66 u64 addr = 0; ///< Address (file-format-specific).
67 u64 vsize = 0; ///< Size of virtual section, otherwise data.size() is valid.
68 u32 type = 0; ///< Type (file-format-specific).
69 u32 flags = 0; ///< Flags (file-format-specific).
70 u32 name = 0; ///< Name (file-format-specific, can also be index, etc.).
71 u32 align = 1; ///< Alignment (bytes).
72
73 /// Section symbol, or signature symbol for SHT_GROUP sections.
74 SymRef sym;
75
76private:
77 SecRef sec_ref;
78
79 util::SmallVector<Relocation, 4> relocs;
80
81public:
82 /// Generic field for target-specific data.
83 void *target_info = nullptr;
84
85 /// Whether the section is virtual, i.e. has no content.
86 bool is_virtual;
87
88private:
89 /// Whether the section can have relocations. For ELF, this implies that the
90 /// immediately following section ID is reserved as relocation section and
91 /// that name-5..name is ".rela".
92 bool has_relocs;
93
94public:
95#ifndef NDEBUG
96 /// Whether the section is currently in use by a SectionWriter.
97 bool locked = false;
98#endif
99
100 DataSection(SecRef ref) noexcept : sec_ref(ref) {}
101
102 SecRef get_ref() const noexcept { return sec_ref; }
103
104 size_t size() const { return is_virtual ? vsize : data.size(); }
105
106 template <typename T>
107 void write(const T &t) noexcept {
108 assert(!locked);
109 assert(!is_virtual);
110 size_t off = data.size();
111 data.resize_uninitialized(data.size() + sizeof(T));
112 std::memcpy(data.data() + off, &t, sizeof(T));
113 }
114};
115
116/// Assembler base class.
117class Assembler {
118public:
119 struct TargetInfo {
120 /// The return address register for the CIE.
121 u8 cie_return_addr_register;
122 /// The initial instructions for the CIE.
123 std::span<const u8> cie_instrs;
124 /// Code alignment factor for the CIE, ULEB128, must be one byte.
125 u8 cie_code_alignment_factor;
126 /// Data alignment factor for the CIE, SLEB128, must be one byte.
127 u8 cie_data_alignment_factor;
128
129 /// The relocation type for 32-bit pc-relative offsets.
130 u32 reloc_pc32;
131 /// The relocation type for 64-bit absolute addresses.
132 u32 reloc_abs64;
133 };
134
135protected:
136 const TargetInfo &target_info;
137
138 util::BumpAllocator<> section_allocator;
139 util::SmallVector<util::BumpAllocUniquePtr<DataSection>, 16> sections;
140
141 Assembler(const TargetInfo &target_info) noexcept
142 : target_info(target_info) {}
143 virtual ~Assembler();
144
145public:
146 virtual void reset() noexcept;
147
148 /// \name Sections
149 /// @{
150
151 DataSection &get_section(SecRef ref) noexcept {
152 assert(ref.valid());
153 return *sections[ref.id()];
154 }
155
156 const DataSection &get_section(SecRef ref) const noexcept {
157 assert(ref.valid());
158 return *sections[ref.id()];
159 }
160
161 /// @}
162
163 /// \name Relocations
164 /// @{
165
166 /// Add relocation. Type is file-format and target-specific.
168 SecRef sec, SymRef sym, u32 type, u32 offset, i64 addend) noexcept {
169 assert(i32(addend) == addend && "non-32-bit addends are unsupported");
170 get_section(sec).relocs.emplace_back(offset, sym, type, addend);
171 }
172
173 void reloc_pc32(SecRef sec, SymRef sym, u32 offset, i64 addend) noexcept {
174 reloc_sec(sec, sym, target_info.reloc_pc32, offset, addend);
175 }
176
177 void reloc_abs(SecRef sec, SymRef sym, u32 offset, i64 addend) noexcept {
178 reloc_sec(sec, sym, target_info.reloc_abs64, offset, addend);
179 }
180
181 /// @}
182
183 virtual void finalize() noexcept {}
184
185 virtual std::vector<u8> build_object_file() noexcept = 0;
186};
187
188} // namespace tpde
189
190#undef ARG
void reloc_sec(SecRef sec, SymRef sym, u32 type, u32 offset, i64 addend) noexcept
Add relocation. Type is file-format and target-specific.