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