Skip to content

Commit

Permalink
modernize the PIE patch for the latest master branch
Browse files Browse the repository at this point in the history
This is the part of #3960 that has to be rewritten to apply to latest
master branch code.
  • Loading branch information
andrewrk committed Nov 23, 2020
1 parent 55ab50e commit abc717f
Show file tree
Hide file tree
Showing 18 changed files with 272 additions and 15 deletions.
148 changes: 148 additions & 0 deletions lib/libc/musl/ldso/dlstart.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include <stddef.h>
#include "dynlink.h"
#include "libc.h"

#ifndef START
#define START "_dlstart"
#endif

#define SHARED

#include "crt_arch.h"

#ifndef GETFUNCSYM
#define GETFUNCSYM(fp, sym, got) do { \
hidden void sym(); \
static void (*static_func_ptr)() = sym; \
__asm__ __volatile__ ( "" : "+m"(static_func_ptr) : : "memory"); \
*(fp) = static_func_ptr; } while(0)
#endif

hidden void _dlstart_c(size_t *sp, size_t *dynv)
{
size_t i, aux[AUX_CNT], dyn[DYN_CNT];
size_t *rel, rel_size, base;

int argc = *sp;
char **argv = (void *)(sp+1);

for (i=argc+1; argv[i]; i++);
size_t *auxv = (void *)(argv+i+1);

for (i=0; i<AUX_CNT; i++) aux[i] = 0;
for (i=0; auxv[i]; i+=2) if (auxv[i]<AUX_CNT)
aux[auxv[i]] = auxv[i+1];

#if DL_FDPIC
struct fdpic_loadseg *segs, fakeseg;
size_t j;
if (dynv) {
/* crt_arch.h entry point asm is responsible for reserving
* space and moving the extra fdpic arguments to the stack
* vector where they are easily accessible from C. */
segs = ((struct fdpic_loadmap *)(sp[-1] ? sp[-1] : sp[-2]))->segs;
} else {
/* If dynv is null, the entry point was started from loader
* that is not fdpic-aware. We can assume normal fixed-
* displacement ELF loading was performed, but when ldso was
* run as a command, finding the Ehdr is a heursitic: we
* have to assume Phdrs start in the first 4k of the file. */
base = aux[AT_BASE];
if (!base) base = aux[AT_PHDR] & -4096;
segs = &fakeseg;
segs[0].addr = base;
segs[0].p_vaddr = 0;
segs[0].p_memsz = -1;
Ehdr *eh = (void *)base;
Phdr *ph = (void *)(base + eh->e_phoff);
size_t phnum = eh->e_phnum;
size_t phent = eh->e_phentsize;
while (phnum-- && ph->p_type != PT_DYNAMIC)
ph = (void *)((size_t)ph + phent);
dynv = (void *)(base + ph->p_vaddr);
}
#endif

for (i=0; i<DYN_CNT; i++) dyn[i] = 0;
for (i=0; dynv[i]; i+=2) if (dynv[i]<DYN_CNT)
dyn[dynv[i]] = dynv[i+1];

#if DL_FDPIC
for (i=0; i<DYN_CNT; i++) {
if (i==DT_RELASZ || i==DT_RELSZ) continue;
if (!dyn[i]) continue;
for (j=0; dyn[i]-segs[j].p_vaddr >= segs[j].p_memsz; j++);
dyn[i] += segs[j].addr - segs[j].p_vaddr;
}
base = 0;

const Sym *syms = (void *)dyn[DT_SYMTAB];

rel = (void *)dyn[DT_RELA];
rel_size = dyn[DT_RELASZ];
for (; rel_size; rel+=3, rel_size-=3*sizeof(size_t)) {
if (!IS_RELATIVE(rel[1], syms)) continue;
for (j=0; rel[0]-segs[j].p_vaddr >= segs[j].p_memsz; j++);
size_t *rel_addr = (void *)
(rel[0] + segs[j].addr - segs[j].p_vaddr);
if (R_TYPE(rel[1]) == REL_FUNCDESC_VAL) {
*rel_addr += segs[rel_addr[1]].addr
- segs[rel_addr[1]].p_vaddr
+ syms[R_SYM(rel[1])].st_value;
rel_addr[1] = dyn[DT_PLTGOT];
} else {
size_t val = syms[R_SYM(rel[1])].st_value;
for (j=0; val-segs[j].p_vaddr >= segs[j].p_memsz; j++);
*rel_addr = rel[2] + segs[j].addr - segs[j].p_vaddr + val;
}
}
#else
/* If the dynamic linker is invoked as a command, its load
* address is not available in the aux vector. Instead, compute
* the load address as the difference between &_DYNAMIC and the
* virtual address in the PT_DYNAMIC program header. */
base = aux[AT_BASE];
if (!base) {
size_t phnum = aux[AT_PHNUM];
size_t phentsize = aux[AT_PHENT];
Phdr *ph = (void *)aux[AT_PHDR];
for (i=phnum; i--; ph = (void *)((char *)ph + phentsize)) {
if (ph->p_type == PT_DYNAMIC) {
base = (size_t)dynv - ph->p_vaddr;
break;
}
}
}

/* MIPS uses an ugly packed form for GOT relocations. Since we
* can't make function calls yet and the code is tiny anyway,
* it's simply inlined here. */
if (NEED_MIPS_GOT_RELOCS) {
size_t local_cnt = 0;
size_t *got = (void *)(base + dyn[DT_PLTGOT]);
for (i=0; dynv[i]; i+=2) if (dynv[i]==DT_MIPS_LOCAL_GOTNO)
local_cnt = dynv[i+1];
for (i=0; i<local_cnt; i++) got[i] += base;
}

rel = (void *)(base+dyn[DT_REL]);
rel_size = dyn[DT_RELSZ];
for (; rel_size; rel+=2, rel_size-=2*sizeof(size_t)) {
if (!IS_RELATIVE(rel[1], 0)) continue;
size_t *rel_addr = (void *)(base + rel[0]);
*rel_addr += base;
}

rel = (void *)(base+dyn[DT_RELA]);
rel_size = dyn[DT_RELASZ];
for (; rel_size; rel+=3, rel_size-=3*sizeof(size_t)) {
if (!IS_RELATIVE(rel[1], 0)) continue;
size_t *rel_addr = (void *)(base + rel[0]);
*rel_addr = base + rel[2];
}
#endif

stage2_func dls2;
GETFUNCSYM(&dls2, __dls2, base+dyn[DT_PLTGOT]);
dls2((void *)base, sp);
}
11 changes: 11 additions & 0 deletions lib/std/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,9 @@ pub const LibExeObjStep = struct {
/// Position Independent Code
force_pic: ?bool = null,

/// Position Independent Executable
pie: ?bool = null,

subsystem: ?builtin.SubSystem = null,

const LinkObject = union(enum) {
Expand Down Expand Up @@ -2307,6 +2310,14 @@ pub const LibExeObjStep = struct {
}
}

if (self.pie) |pie| {
if (pie) {
try zig_args.append("-fPIE");
} else {
try zig_args.append("-fno-PIE");
}
}

if (self.subsystem) |subsystem| {
try zig_args.append("--subsystem");
try zig_args.append(switch (subsystem) {
Expand Down
2 changes: 1 addition & 1 deletion lib/std/dynamic_library.zig
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const RDebug = extern struct {
extern var _DYNAMIC: [128]elf.Dyn;

comptime {
if (builtin.os == .linux) {
if (std.Target.current.os.tag == .linux) {
asm (
\\ .weak _DYNAMIC
\\ .hidden _DYNAMIC
Expand Down
12 changes: 6 additions & 6 deletions lib/std/os/linux/start_pie.zig
Original file line number Diff line number Diff line change
Expand Up @@ -111,25 +111,25 @@ pub fn apply_relocations() void {
var i: usize = 0;
while (dynv[i].d_tag != elf.DT_NULL) : (i += 1) {
switch (dynv[i].d_tag) {
elf.DT_REL => rel_addr = base_addr + dynv[i].d_un.d_ptr,
elf.DT_RELA => rela_addr = base_addr + dynv[i].d_un.d_ptr,
elf.DT_RELSZ => rel_size = dynv[i].d_un.d_val,
elf.DT_RELASZ => rela_size = dynv[i].d_un.d_val,
elf.DT_REL => rel_addr = base_addr + dynv[i].d_val,
elf.DT_RELA => rela_addr = base_addr + dynv[i].d_val,
elf.DT_RELSZ => rel_size = dynv[i].d_val,
elf.DT_RELASZ => rela_size = dynv[i].d_val,
else => {},
}
}
}

// Perform the relocations
if (rel_addr != 0) {
const rel = @bytesToSlice(elf.Rel, @intToPtr([*]u8, rel_addr)[0..rel_size]);
const rel = std.mem.bytesAsSlice(elf.Rel, @intToPtr([*]u8, rel_addr)[0..rel_size]);
for (rel) |r| {
if (r.r_type() != ARCH_RELATIVE_RELOC) continue;
@intToPtr(*usize, base_addr + r.r_offset).* += base_addr;
}
}
if (rela_addr != 0) {
const rela = @bytesToSlice(elf.Rela, @intToPtr([*]u8, rela_addr)[0..rela_size]);
const rela = std.mem.bytesAsSlice(elf.Rela, @intToPtr([*]u8, rela_addr)[0..rela_size]);
for (rela) |r| {
if (r.r_type() != ARCH_RELATIVE_RELOC) continue;
@intToPtr(*usize, base_addr + r.r_offset).* += base_addr + @bitCast(usize, r.r_addend);
Expand Down
33 changes: 29 additions & 4 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ pub const InitOptions = struct {
link_libc: bool = false,
link_libcpp: bool = false,
want_pic: ?bool = null,
/// This means that if the output mode is an executable it will be a
/// Position Independent Executable. If the output mode is not an
/// executable this field is ignored.
want_pie: ?bool = null,
want_sanitize_c: ?bool = null,
want_stack_check: ?bool = null,
want_valgrind: ?bool = null,
Expand Down Expand Up @@ -527,17 +531,30 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
options.libc_installation,
);

const must_pie = target_util.requiresPIE(options.target);
const pie = if (options.want_pie) |explicit| pie: {
if (!explicit and must_pie) {
return error.TargetRequiresPIE;
}
break :pie explicit;
} else must_pie;

const must_pic: bool = b: {
if (target_util.requiresPIC(options.target, link_libc))
break :b true;
break :b link_mode == .Dynamic;
};
const pic = if (options.want_pic) |explicit| pic: {
if (!explicit and must_pic) {
return error.TargetRequiresPIC;
if (!explicit) {
if (must_pic) {
return error.TargetRequiresPIC;
}
if (pie) {
return error.PIERequiresPIC;
}
}
break :pic explicit;
} else must_pic;
} else pie or must_pic;

// Make a decision on whether to use Clang for translate-c and compiling C files.
const use_clang = if (options.use_clang) |explicit| explicit else blk: {
Expand Down Expand Up @@ -618,6 +635,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
cache.hash.add(options.target.abi);
cache.hash.add(ofmt);
cache.hash.add(pic);
cache.hash.add(pie);
cache.hash.add(stack_check);
cache.hash.add(link_mode);
cache.hash.add(options.function_sections);
Expand Down Expand Up @@ -814,6 +832,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.version = options.version,
.libc_installation = libc_dirs.libc_installation,
.pic = pic,
.pie = pie,
.valgrind = valgrind,
.stack_check = stack_check,
.single_threaded = single_threaded,
Expand Down Expand Up @@ -898,7 +917,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
try comp.addBuildingGLibCJobs();
}
if (comp.wantBuildMuslFromSource()) {
try comp.work_queue.ensureUnusedCapacity(5);
try comp.work_queue.ensureUnusedCapacity(6);
if (target_util.libc_needs_crti_crtn(comp.getTarget())) {
comp.work_queue.writeAssumeCapacity(&[_]Job{
.{ .musl_crt_file = .crti_o },
Expand All @@ -908,6 +927,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
comp.work_queue.writeAssumeCapacity(&[_]Job{
.{ .musl_crt_file = .crt1_o },
.{ .musl_crt_file = .scrt1_o },
.{ .musl_crt_file = .rcrt1_o },
.{ .musl_crt_file = .libc_a },
});
}
Expand Down Expand Up @@ -2473,6 +2493,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
\\pub const have_error_return_tracing = {};
\\pub const valgrind_support = {};
\\pub const position_independent_code = {};
\\pub const position_independent_executable = {};
\\pub const strip_debug_info = {};
\\pub const code_model = CodeModel.{};
\\
Expand All @@ -2484,6 +2505,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8
comp.bin_file.options.error_return_tracing,
comp.bin_file.options.valgrind,
comp.bin_file.options.pic,
comp.bin_file.options.pie,
comp.bin_file.options.strip,
@tagName(comp.bin_file.options.machine_code_model),
});
Expand Down Expand Up @@ -2587,6 +2609,7 @@ fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CR
.want_stack_check = false,
.want_valgrind = false,
.want_pic = comp.bin_file.options.pic,
.want_pie = comp.bin_file.options.pie,
.emit_h = null,
.strip = comp.bin_file.options.strip,
.is_native_os = comp.bin_file.options.is_native_os,
Expand Down Expand Up @@ -2795,6 +2818,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
.subsystem = subsystem,
.err_color = @enumToInt(comp.color),
.pic = comp.bin_file.options.pic,
.pie = comp.bin_file.options.pie,
.link_libc = comp.bin_file.options.link_libc,
.link_libcpp = comp.bin_file.options.link_libcpp,
.strip = comp.bin_file.options.strip,
Expand Down Expand Up @@ -2950,6 +2974,7 @@ pub fn build_crt_file(
.want_stack_check = false,
.want_valgrind = false,
.want_pic = comp.bin_file.options.pic,
.want_pie = comp.bin_file.options.pie,
.emit_h = null,
.strip = comp.bin_file.options.strip,
.is_native_os = comp.bin_file.options.is_native_os,
Expand Down
18 changes: 16 additions & 2 deletions src/clang_options_data.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2460,7 +2460,14 @@ sepd1("exported_symbols_list"),
.pd2 = false,
.psl = false,
},
flagpd1("fPIE"),
.{
.name = "fPIE",
.syntax = .flag,
.zig_equivalent = .pie,
.pd1 = true,
.pd2 = false,
.psl = false,
},
flagpd1("fno-access-control"),
flagpd1("faddrsig"),
flagpd1("faggressive-function-elimination"),
Expand Down Expand Up @@ -2775,7 +2782,14 @@ flagpd1("fnext-runtime"),
.pd2 = false,
.psl = false,
},
flagpd1("fno-PIE"),
.{
.name = "fno-PIE",
.syntax = .flag,
.zig_equivalent = .no_pie,
.pd1 = true,
.pd2 = false,
.psl = false,
},
flagpd1("fno-no-access-control"),
flagpd1("fno-addrsig"),
flagpd1("fno-aggressive-function-elimination"),
Expand Down
2 changes: 2 additions & 0 deletions src/libcxx.zig
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ pub fn buildLibCXX(comp: *Compilation) !void {
.want_stack_check = false,
.want_valgrind = false,
.want_pic = comp.bin_file.options.pic,
.want_pie = comp.bin_file.options.pie,
.emit_h = null,
.strip = comp.bin_file.options.strip,
.is_native_os = comp.bin_file.options.is_native_os,
Expand Down Expand Up @@ -288,6 +289,7 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
.want_stack_check = false,
.want_valgrind = false,
.want_pic = comp.bin_file.options.pic,
.want_pie = comp.bin_file.options.pie,
.emit_h = null,
.strip = comp.bin_file.options.strip,
.is_native_os = comp.bin_file.options.is_native_os,
Expand Down
1 change: 1 addition & 0 deletions src/libunwind.zig
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub fn buildStaticLib(comp: *Compilation) !void {
.want_stack_check = false,
.want_valgrind = false,
.want_pic = comp.bin_file.options.pic,
.want_pie = comp.bin_file.options.pie,
.emit_h = null,
.strip = comp.bin_file.options.strip,
.is_native_os = comp.bin_file.options.is_native_os,
Expand Down
1 change: 1 addition & 0 deletions src/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub const Options = struct {
bind_global_refs_locally: bool,
is_native_os: bool,
pic: bool,
pie: bool,
valgrind: bool,
stack_check: bool,
single_threaded: bool,
Expand Down

0 comments on commit abc717f

Please sign in to comment.