TPDE
Loading...
Searching...
No Matches
AssemblerElfA64.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/AssemblerElf.hpp"
7#include "tpde/util/SegmentedVector.hpp"
8#include "tpde/util/SmallVector.hpp"
9#include <disarm64.h>
10
11namespace tpde::a64 {
12
13/// The AArch64-specific implementation for the AssemblerElf
14struct AssemblerElfA64 : AssemblerElf<AssemblerElfA64> {
16
17 static const TargetInfo TARGET_INFO;
18
19 class SectionWriter : public Base::SectionWriterBase<SectionWriter> {
20 public:
21 void more_space(u32 size) noexcept;
22
23 bool try_write_inst(u32 inst) noexcept {
24 if (inst == 0) {
25 return false;
26 }
27 write(inst);
28 return true;
29 }
30
31 void write_inst(u32 inst) noexcept {
32 assert(inst != 0);
33 write(inst);
34 }
35
36 void write_inst_unchecked(u32 inst) noexcept {
37 assert(inst != 0);
38 write_unchecked(inst);
39 }
40 };
41
42 enum class UnresolvedEntryKind : u8 {
43 BR,
44 COND_BR,
45 TEST_BR,
46 JUMP_TABLE,
47 };
48
49 /// Information about veneers and unresolved branches for a section.
50 struct VeneerInfo {
51 /// Begin offsets of veneer space. A veneer always has space for all
52 /// unresolved cbz/tbz branches that come after it.
53 util::SmallVector<u32, 16> veneers;
54 u32 unresolved_test_brs = 0, unresolved_cond_brs = 0;
55 };
56
58
59 explicit AssemblerElfA64() = default;
60
61private:
62 VeneerInfo &get_veneer_info(DataSection &section) noexcept {
63 if (!section.target_info) [[unlikely]] {
64 section.target_info = &veneer_infos.emplace_back();
65 }
66 return *static_cast<VeneerInfo *>(section.target_info);
67 }
68
69public:
70 void add_unresolved_entry(Label label,
71 SecRef sec,
72 u32 off,
73 UnresolvedEntryKind kind) noexcept {
74 AssemblerElfBase::reloc_sec(sec, label, static_cast<u8>(kind), off);
75 if (kind == UnresolvedEntryKind::COND_BR) {
76 get_veneer_info(get_section(sec)).unresolved_cond_brs++;
77 } else if (kind == UnresolvedEntryKind::TEST_BR) {
78 get_veneer_info(get_section(sec)).unresolved_test_brs++;
79 }
80 }
81
82 void handle_fixup(const TempSymbolInfo &info,
83 const TempSymbolFixup &fixup) noexcept;
84
85 void reset() noexcept;
86};
87
88inline void
89 AssemblerElfA64::handle_fixup(const TempSymbolInfo &info,
90 const TempSymbolFixup &fixup) noexcept {
91 // TODO: emit relocations when fixup is in different section
92 assert(info.section == fixup.section && "multi-text section not supported");
93 DataSection &section = get_section(fixup.section);
94 VeneerInfo &vi = get_veneer_info(section);
95 auto &veneers = vi.veneers;
96
97 u8 *section_data = section.data.data();
98 u32 *dst_ptr = reinterpret_cast<u32 *>(section_data + fixup.off);
99
100 auto fix_condbr = [&](unsigned nbits) {
101 i64 diff = (i64)info.off - (i64)fixup.off;
102 assert(diff >= 0 && diff < 128 * 1024 * 1024);
103 // lowest two bits are ignored, highest bit is sign bit
104 if (diff >= (4 << (nbits - 1))) {
105 auto veneer = std::lower_bound(veneers.begin(), veneers.end(), fixup.off);
106 assert(veneer != veneers.end());
107
108 // Create intermediate branch at v.begin
109 auto *br = reinterpret_cast<u32 *>(section_data + *veneer);
110 assert(*br == 0 && "overwriting instructions with veneer branch");
111 *br = de64_B((info.off - *veneer) / 4);
112 diff = *veneer - fixup.off;
113 *veneer += 4;
114 }
115 u32 off_mask = ((1 << nbits) - 1) << 5;
116 *dst_ptr = (*dst_ptr & ~off_mask) | ((diff / 4) << 5);
117 };
118
119 switch (static_cast<UnresolvedEntryKind>(fixup.kind)) {
120 case UnresolvedEntryKind::BR: {
121 // diff from entry to label (should be positive tho)
122 i64 diff = (i64)info.off - (i64)fixup.off;
123 assert(diff >= 0 && diff < 128 * 1024 * 1024);
124 *dst_ptr = de64_B(diff / 4);
125 break;
126 }
127 case UnresolvedEntryKind::COND_BR:
128 if (veneers.empty() || veneers.back() < fixup.off) {
129 assert(vi.unresolved_cond_brs > 0);
130 vi.unresolved_cond_brs -= 1;
131 }
132 fix_condbr(19); // CBZ/CBNZ has 19 bits.
133 break;
134 case UnresolvedEntryKind::TEST_BR:
135 if (veneers.empty() || veneers.back() < fixup.off) {
136 assert(vi.unresolved_test_brs > 0);
137 vi.unresolved_test_brs -= 1;
138 }
139 fix_condbr(14); // TBZ/TBNZ has 14 bits.
140 break;
141 case UnresolvedEntryKind::JUMP_TABLE: {
142 auto table_off = *reinterpret_cast<u32 *>(section_data + fixup.off);
143 *dst_ptr = (i32)info.off - (i32)table_off;
144 break;
145 }
146 }
147}
148
149inline void AssemblerElfA64::SectionWriter::more_space(u32 size) noexcept {
150 if (allocated_size() >= (128 * 1024 * 1024)) {
151 // we do not support multiple text sections currently
152 TPDE_FATAL("AArch64 doesn't support sections larger than 128 MiB");
153 }
154
155 // If the section has no unresolved conditional branch, veneer_info is null.
156 // In that case, we don't need to do anything regarding veneers.
157 VeneerInfo *vi = static_cast<VeneerInfo *>(section->target_info);
158 u32 unresolved_count =
159 vi ? vi->unresolved_test_brs + vi->unresolved_cond_brs : 0;
160 u32 veneer_size = sizeof(u32) * unresolved_count;
161 SectionWriterBase::more_space(size + veneer_size + 4);
162 if (veneer_size == 0) {
163 return;
164 }
165
166 // TBZ has 14 bits, CBZ has 19 bits; but the first bit is the sign bit
167 u32 max_dist = vi->unresolved_test_brs ? 4 << (14 - 1) : 4 << (19 - 1);
168 max_dist -= veneer_size; // must be able to reach last veneer
169 // TODO: get a better approximation of the first unresolved condbr after the
170 // last veneer.
171 u32 first_condbr = vi->veneers.empty() ? 0 : vi->veneers.back();
172 // If all condbrs can only jump inside the now-reserved memory, do nothing.
173 if (first_condbr + max_dist > allocated_size()) {
174 return;
175 }
176
177 u32 cur_off = offset();
178 vi->veneers.push_back(cur_off + 4);
179 vi->unresolved_test_brs = vi->unresolved_cond_brs = 0;
180
181 *reinterpret_cast<u32 *>(data_begin + cur_off) = de64_B(veneer_size / 4 + 1);
182 std::memset(data_begin + cur_off + 4, 0, veneer_size);
183 cur_ptr() += veneer_size + 4;
184}
185
186inline void AssemblerElfA64::reset() noexcept {
187 Base::reset();
188 veneer_infos.clear();
189}
190
191} // namespace tpde::a64
A vector with stable element addresses.
AssemblerElf contains the architecture-independent logic to emit ELF object files (currently linux-sp...
Information about veneers and unresolved branches for a section.
util::SmallVector< u32, 16 > veneers
The AArch64-specific implementation for the AssemblerElf.