authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-07-25 23:55:37+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-07-30 01:26:10+02:00
log2386bfe854826e0726b7002c04cd9fc4c08d68f3
treed0f5634fbea27b77f895b12629fe96b37f156dab
parent68cebde186eb8507509c9418c1ab3b843c6f24ff
signature Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

std.os.linux.start_pie: Rewrite relocate() to avoid jump tables and libcalls.

The code would cause LLVM to emit a jump table for the switch in the loop over the dynamic tags. That jump table was far enough away that the compiler decided to go through the GOT, which would of course break at this early stage as we haven't applied MIPS's local GOT relocations yet, nor can we until we've walked through the _DYNAMIC array. The first attempt at rewriting this used code like this: var sorted_dynv = [_]elf.Addr{0} ** elf.DT_NUM; But this is also problematic as it results in a memcpy() call. Instead, we explicitly initialize it to undefined and use a loop of volatile stores to clear it.

1 files changed, 30 insertions(+), 18 deletions(-)

lib/std/os/linux/start_pie.zig+30-18
...@@ -193,6 +193,7 @@ pub fn relocate(phdrs: []elf.Phdr) void {...@@ -193,6 +193,7 @@ pub fn relocate(phdrs: []elf.Phdr) void {
193 @disableInstrumentation();193 @disableInstrumentation();
194194
195 const dynv = getDynamicSymbol();195 const dynv = getDynamicSymbol();
196
196 // Recover the delta applied by the loader by comparing the effective and197 // Recover the delta applied by the loader by comparing the effective and
197 // the theoretical load addresses for the `_DYNAMIC` symbol.198 // the theoretical load addresses for the `_DYNAMIC` symbol.
198 const base_addr = base: {199 const base_addr = base: {
...@@ -204,34 +205,45 @@ pub fn relocate(phdrs: []elf.Phdr) void {...@@ -204,34 +205,45 @@ pub fn relocate(phdrs: []elf.Phdr) void {
204 @trap();205 @trap();
205 };206 };
206207
207 var rel_addr: usize = 0;208 var sorted_dynv: [elf.DT_NUM]elf.Addr = undefined;
208 var rela_addr: usize = 0;209
209 var rel_size: usize = 0;210 // Zero-initialized this way to prevent the compiler from turning this into
210 var rela_size: usize = 0;211 // `memcpy` or `memset` calls (which can require relocations).
212 for (&sorted_dynv) |*dyn| {
213 const pdyn: *volatile elf.Addr = @ptrCast(dyn);
214 pdyn.* = 0;
215 }
216
211 {217 {
218 // `dynv` has no defined order. Fix that.
212 var i: usize = 0;219 var i: usize = 0;
213 while (dynv[i].d_tag != elf.DT_NULL) : (i += 1) {220 while (dynv[i].d_tag != elf.DT_NULL) : (i += 1) {
214 switch (dynv[i].d_tag) {221 if (dynv[i].d_tag < elf.DT_NUM) sorted_dynv[@bitCast(dynv[i].d_tag)] = dynv[i].d_val;
215 elf.DT_REL => rel_addr = base_addr + dynv[i].d_val,
216 elf.DT_RELA => rela_addr = base_addr + dynv[i].d_val,
217 elf.DT_RELSZ => rel_size = dynv[i].d_val,
218 elf.DT_RELASZ => rela_size = dynv[i].d_val,
219 else => {},
220 }
221 }222 }
222 }223 }
223224
224 // Apply the relocations.225
225 if (rel_addr != 0) {226 // Apply normal relocations.
226 const rel = std.mem.bytesAsSlice(elf.Rel, @as([*]u8, @ptrFromInt(rel_addr))[0..rel_size]);227
227 for (rel) |r| {228 const rel = sorted_dynv[elf.DT_REL];
229 if (rel != 0) {
230 const rels = @call(.always_inline, std.mem.bytesAsSlice, .{
231 elf.Rel,
232 @as([*]u8, @ptrFromInt(base_addr + rel))[0..sorted_dynv[elf.DT_RELSZ]],
233 });
234 for (rels) |r| {
228 if (r.r_type() != R_RELATIVE) continue;235 if (r.r_type() != R_RELATIVE) continue;
229 @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* += base_addr;236 @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* += base_addr;
230 }237 }
231 }238 }
232 if (rela_addr != 0) {239
233 const rela = std.mem.bytesAsSlice(elf.Rela, @as([*]u8, @ptrFromInt(rela_addr))[0..rela_size]);240 const rela = sorted_dynv[elf.DT_RELA];
234 for (rela) |r| {241 if (rela != 0) {
242 const relas = @call(.always_inline, std.mem.bytesAsSlice, .{
243 elf.Rela,
244 @as([*]u8, @ptrFromInt(base_addr + rela))[0..sorted_dynv[elf.DT_RELASZ]],
245 });
246 for (relas) |r| {
235 if (r.r_type() != R_RELATIVE) continue;247 if (r.r_type() != R_RELATIVE) continue;
236 @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* = base_addr + @as(usize, @bitCast(r.r_addend));248 @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* = base_addr + @as(usize, @bitCast(r.r_addend));
237 }249 }