elf: port more linker tests

This commit is contained in:
Jakub Konka 2023-10-09 14:23:57 +02:00
parent 1efc0519ce
commit 9487007ffe

View File

@ -27,6 +27,8 @@ pub fn build(b: *Build) void {
elf_step.dependOn(testEmptyObject(b, .{ .target = musl_target }));
elf_step.dependOn(testEntryPoint(b, .{ .target = musl_target }));
elf_step.dependOn(testGcSections(b, .{ .target = musl_target }));
elf_step.dependOn(testImageBase(b, .{ .target = musl_target }));
elf_step.dependOn(testInitArrayOrder(b, .{ .target = musl_target }));
elf_step.dependOn(testLinkingC(b, .{ .target = musl_target }));
elf_step.dependOn(testLinkingCpp(b, .{ .target = musl_target }));
elf_step.dependOn(testLinkingZig(b, .{ .target = musl_target }));
@ -44,8 +46,24 @@ pub fn build(b: *Build) void {
elf_step.dependOn(testDsoUndef(b, .{ .target = glibc_target }));
elf_step.dependOn(testExportDynamic(b, .{ .target = glibc_target }));
elf_step.dependOn(testExportSymbolsFromExe(b, .{ .target = glibc_target }));
// https://github.com/ziglang/zig/issues/17430
// elf_step.dependOn(testFuncAddress(b, .{ .target = glibc_target }));
elf_step.dependOn(testHiddenWeakUndef(b, .{ .target = glibc_target }));
elf_step.dependOn(testIFuncAlias(b, .{ .target = glibc_target }));
// https://github.com/ziglang/zig/issues/17430
// elf_step.dependOn(testIFuncDlopen(b, .{ .target = glibc_target }));
elf_step.dependOn(testIFuncDso(b, .{ .target = glibc_target }));
elf_step.dependOn(testIFuncDynamic(b, .{ .target = glibc_target }));
elf_step.dependOn(testIFuncExport(b, .{ .target = glibc_target }));
elf_step.dependOn(testIFuncFuncPtr(b, .{ .target = glibc_target }));
elf_step.dependOn(testIFuncNoPlt(b, .{ .target = glibc_target }));
// https://github.com/ziglang/zig/issues/17430 ??
// elf_step.dependOn(testIFuncStatic(b, .{ .target = glibc_target }));
// elf_step.dependOn(testIFuncStaticPie(b, .{ .target = glibc_target }));
elf_step.dependOn(testInitArrayOrder(b, .{ .target = glibc_target }));
elf_step.dependOn(testLargeAlignmentDso(b, .{ .target = glibc_target }));
elf_step.dependOn(testLargeAlignmentExe(b, .{ .target = glibc_target }));
elf_step.dependOn(testPie(b, .{ .target = glibc_target }));
}
fn testAbsSymbols(b: *Build, opts: Options) *Step {
@ -686,6 +704,32 @@ fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step {
return test_step;
}
fn testFuncAddress(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "func-address", opts);
const dso = addSharedLibrary(b, "a", opts);
addCSourceBytes(dso, "void fn() {}", &.{});
const exe = addExecutable(b, "main", opts);
addCSourceBytes(exe,
\\#include <assert.h>
\\typedef void Func();
\\void fn();
\\Func *const ptr = fn;
\\int main() {
\\ assert(fn == ptr);
\\}
, &.{});
exe.linkLibrary(dso);
exe.force_pic = false;
exe.pie = false;
const run = addRunArtifact(exe);
test_step.dependOn(&run.step);
return test_step;
}
fn testGcSections(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "gc-sections", opts);
@ -776,6 +820,449 @@ fn testGcSections(b: *Build, opts: Options) *Step {
return test_step;
}
fn testHiddenWeakUndef(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "hidden-weak-undef", opts);
const dso = addSharedLibrary(b, "a", opts);
addCSourceBytes(dso,
\\__attribute__((weak, visibility("hidden"))) void foo();
\\void bar() { foo(); }
, &.{});
const check = dso.checkObject();
check.checkInDynamicSymtab();
check.checkNotPresent("foo");
check.checkInDynamicSymtab();
check.checkContains("bar");
test_step.dependOn(&check.step);
return test_step;
}
fn testIFuncAlias(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ifunc-alias", opts);
const exe = addExecutable(b, "main", opts);
addCSourceBytes(exe,
\\#include <assert.h>
\\void foo() {}
\\int bar() __attribute__((ifunc("resolve_bar")));
\\void *resolve_bar() { return foo; }
\\void *bar2 = bar;
\\int main() {
\\ assert(bar == bar2);
\\}
, &.{});
exe.force_pic = true;
exe.linkLibC();
const run = addRunArtifact(exe);
run.expectExitCode(0);
test_step.dependOn(&run.step);
return test_step;
}
fn testIFuncDlopen(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ifunc-dlopen", opts);
const dso = addSharedLibrary(b, "a", opts);
addCSourceBytes(dso,
\\__attribute__((ifunc("resolve_foo")))
\\void foo(void);
\\static void real_foo(void) {
\\}
\\typedef void Func();
\\static Func *resolve_foo(void) {
\\ return real_foo;
\\}
, &.{});
const exe = addExecutable(b, "main", opts);
addCSourceBytes(exe,
\\#include <dlfcn.h>
\\#include <assert.h>
\\#include <stdlib.h>
\\typedef void Func();
\\void foo(void);
\\int main() {
\\ void *handle = dlopen(NULL, RTLD_NOW);
\\ Func *p = dlsym(handle, "foo");
\\
\\ foo();
\\ p();
\\ assert(foo == p);
\\}
, &.{});
exe.linkLibrary(dso);
exe.linkLibC();
exe.linkSystemLibrary2("dl", .{});
exe.force_pic = false;
exe.pie = false;
const run = addRunArtifact(exe);
test_step.dependOn(&run.step);
return test_step;
}
fn testIFuncDso(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ifunc-dso", opts);
const dso = addSharedLibrary(b, "a", opts);
addCSourceBytes(dso,
\\#include<stdio.h>
\\__attribute__((ifunc("resolve_foobar")))
\\void foobar(void);
\\static void real_foobar(void) {
\\ printf("Hello world\n");
\\}
\\typedef void Func();
\\static Func *resolve_foobar(void) {
\\ return real_foobar;
\\}
, &.{});
dso.linkLibC();
const exe = addExecutable(b, "main", opts);
addCSourceBytes(exe,
\\void foobar(void);
\\int main() {
\\ foobar();
\\}
, &.{});
exe.linkLibrary(dso);
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world\n");
test_step.dependOn(&run.step);
return test_step;
}
fn testIFuncDynamic(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ifunc-dynamic", opts);
const main_c =
\\#include <stdio.h>
\\__attribute__((ifunc("resolve_foobar")))
\\static void foobar(void);
\\static void real_foobar(void) {
\\ printf("Hello world\n");
\\}
\\typedef void Func();
\\static Func *resolve_foobar(void) {
\\ return real_foobar;
\\}
\\int main() {
\\ foobar();
\\}
;
{
const exe = addExecutable(b, "main", opts);
addCSourceBytes(exe, main_c, &.{});
exe.linkLibC();
exe.link_z_lazy = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world\n");
test_step.dependOn(&run.step);
}
{
const exe = addExecutable(b, "other", opts);
addCSourceBytes(exe, main_c, &.{});
exe.linkLibC();
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world\n");
test_step.dependOn(&run.step);
}
return test_step;
}
fn testIFuncExport(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ifunc-export", opts);
const dso = addSharedLibrary(b, "a", opts);
addCSourceBytes(dso,
\\#include <stdio.h>
\\__attribute__((ifunc("resolve_foobar")))
\\void foobar(void);
\\void real_foobar(void) {
\\ printf("Hello world\n");
\\}
\\typedef void Func();
\\Func *resolve_foobar(void) {
\\ return real_foobar;
\\}
, &.{});
dso.linkLibC();
const check = dso.checkObject();
check.checkInDynamicSymtab();
check.checkContains("IFUNC GLOBAL DEFAULT foobar");
test_step.dependOn(&check.step);
return test_step;
}
fn testIFuncFuncPtr(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ifunc-func-ptr", opts);
const exe = addExecutable(b, "main", opts);
addCSourceBytes(exe,
\\typedef int Fn();
\\int foo() __attribute__((ifunc("resolve_foo")));
\\int real_foo() { return 3; }
\\Fn *resolve_foo(void) {
\\ return real_foo;
\\}
, &.{});
addCSourceBytes(exe,
\\typedef int Fn();
\\int foo();
\\Fn *get_foo() { return foo; }
, &.{});
addCSourceBytes(exe,
\\#include <stdio.h>
\\typedef int Fn();
\\Fn *get_foo();
\\int main() {
\\ Fn *f = get_foo();
\\ printf("%d\n", f());
\\}
, &.{});
exe.force_pic = true;
exe.linkLibC();
const run = addRunArtifact(exe);
run.expectStdOutEqual("3\n");
test_step.dependOn(&run.step);
return test_step;
}
fn testIFuncNoPlt(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ifunc-noplt", opts);
const exe = addExecutable(b, "main", opts);
addCSourceBytes(exe,
\\#include <stdio.h>
\\__attribute__((ifunc("resolve_foo")))
\\void foo(void);
\\void hello(void) {
\\ printf("Hello world\n");
\\}
\\typedef void Fn();
\\Fn *resolve_foo(void) {
\\ return hello;
\\}
\\int main() {
\\ foo();
\\}
, &.{"-fno-plt"});
exe.force_pic = true;
exe.linkLibC();
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world\n");
test_step.dependOn(&run.step);
return test_step;
}
fn testIFuncStatic(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ifunc-static", opts);
const exe = addExecutable(b, "main", opts);
addCSourceBytes(exe,
\\#include <stdio.h>
\\void foo() __attribute__((ifunc("resolve_foo")));
\\void hello() {
\\ printf("Hello world\n");
\\}
\\void *resolve_foo() {
\\ return hello;
\\}
\\int main() {
\\ foo();
\\ return 0;
\\}
, &.{});
exe.linkLibC();
exe.linkage = .static;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world\n");
test_step.dependOn(&run.step);
return test_step;
}
fn testIFuncStaticPie(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "ifunc-static-pie", opts);
const exe = addExecutable(b, "main", opts);
addCSourceBytes(exe,
\\#include <stdio.h>
\\void foo() __attribute__((ifunc("resolve_foo")));
\\void hello() {
\\ printf("Hello world\n");
\\}
\\void *resolve_foo() {
\\ return hello;
\\}
\\int main() {
\\ foo();
\\ return 0;
\\}
, &.{});
exe.linkage = .static;
exe.force_pic = true;
exe.pie = true;
exe.linkLibC();
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello world\n");
test_step.dependOn(&run.step);
const check = exe.checkObject();
check.checkStart();
check.checkExact("header");
check.checkExact("type DYN");
check.checkStart();
check.checkExact("section headers");
check.checkExact("name .dynamic");
check.checkStart();
check.checkExact("section headers");
check.checkNotPresent("name .interp");
test_step.dependOn(&check.step);
return test_step;
}
fn testImageBase(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "image-base", opts);
{
const exe = addExecutable(b, "main1", opts);
addCSourceBytes(exe,
\\#include <stdio.h>
\\int main() {
\\ printf("Hello World!\n");
\\ return 0;
\\}
, &.{});
exe.linkLibC();
exe.image_base = 0x8000000;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello World!\n");
test_step.dependOn(&run.step);
const check = exe.checkObject();
check.checkStart();
check.checkExact("header");
check.checkExtract("entry {addr}");
check.checkComputeCompare("addr", .{ .op = .gte, .value = .{ .literal = 0x8000000 } });
test_step.dependOn(&check.step);
}
{
const exe = addExecutable(b, "main2", opts);
addCSourceBytes(exe, "void _start() {}", &.{});
exe.image_base = 0xffffffff8000000;
const check = exe.checkObject();
check.checkStart();
check.checkExact("header");
check.checkExtract("entry {addr}");
check.checkComputeCompare("addr", .{ .op = .gte, .value = .{ .literal = 0xffffffff8000000 } });
test_step.dependOn(&check.step);
}
return test_step;
}
fn testInitArrayOrder(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "init-array-order", opts);
const a_o = addObject(b, "a", opts);
addCSourceBytes(a_o,
\\#include <stdio.h>
\\__attribute__((constructor(10000))) void init4() { printf("1"); }
, &.{});
a_o.linkLibC();
const b_o = addObject(b, "b", opts);
addCSourceBytes(b_o,
\\#include <stdio.h>
\\__attribute__((constructor(1000))) void init3() { printf("2"); }
, &.{});
b_o.linkLibC();
const c_o = addObject(b, "c", opts);
addCSourceBytes(c_o,
\\#include <stdio.h>
\\__attribute__((constructor)) void init1() { printf("3"); }
, &.{});
c_o.linkLibC();
const d_o = addObject(b, "d", opts);
addCSourceBytes(d_o,
\\#include <stdio.h>
\\__attribute__((constructor)) void init2() { printf("4"); }
, &.{});
d_o.linkLibC();
const e_o = addObject(b, "e", opts);
addCSourceBytes(e_o,
\\#include <stdio.h>
\\__attribute__((destructor(10000))) void fini4() { printf("5"); }
, &.{});
e_o.linkLibC();
const f_o = addObject(b, "f", opts);
addCSourceBytes(f_o,
\\#include <stdio.h>
\\__attribute__((destructor(1000))) void fini3() { printf("6"); }
, &.{});
f_o.linkLibC();
const g_o = addObject(b, "g", opts);
addCSourceBytes(g_o,
\\#include <stdio.h>
\\__attribute__((destructor)) void fini1() { printf("7"); }
, &.{});
g_o.linkLibC();
const h_o = addObject(b, "h", opts);
addCSourceBytes(h_o,
\\#include <stdio.h>
\\__attribute__((destructor)) void fini2() { printf("8"); }
, &.{});
h_o.linkLibC();
const exe = addExecutable(b, "main", opts);
addCSourceBytes(exe, "int main() { return 0; }", &.{});
exe.addObject(a_o);
exe.addObject(b_o);
exe.addObject(c_o);
exe.addObject(d_o);
exe.addObject(e_o);
exe.addObject(f_o);
exe.addObject(g_o);
exe.addObject(h_o);
const run = addRunArtifact(exe);
run.expectStdOutEqual("21348756");
test_step.dependOn(&run.step);
return test_step;
}
fn testLargeAlignmentDso(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "large-alignment-dso", opts);
@ -951,6 +1438,37 @@ fn testLinkingZig(b: *Build, opts: Options) *Step {
return test_step;
}
fn testPie(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "hello-pie", opts);
const exe = addExecutable(b, "main", opts);
addCSourceBytes(exe,
\\#include <stdio.h>
\\int main() {
\\ printf("Hello!\n");
\\ return 0;
\\}
, &.{});
exe.linkLibC();
exe.force_pic = true;
exe.pie = true;
const run = addRunArtifact(exe);
run.expectStdOutEqual("Hello!\n");
test_step.dependOn(&run.step);
const check = exe.checkObject();
check.checkStart();
check.checkExact("header");
check.checkExact("type DYN");
check.checkStart();
check.checkExact("section headers");
check.checkExact("name .dynamic");
test_step.dependOn(&check.step);
return test_step;
}
fn testTlsStatic(b: *Build, opts: Options) *Step {
const test_step = addTestStep(b, "tls-static", opts);