aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2018-06-16 17:01:23 -0400
committerAndrew Kelley <superjoe30@gmail.com>2018-06-16 17:01:23 -0400
commit48de57d8248d9203b44d28d7749b5d7c1a00deba (patch)
tree149fb86d959501ecb2e5e2aac7f688265f729ba6
parentb3a3e2094e8b88e40eecf65d29f39af10442bde4 (diff)
downloadzig-48de57d8248d9203b44d28d7749b5d7c1a00deba.tar.gz
zig-48de57d8248d9203b44d28d7749b5d7c1a00deba.zip
add basic std lib code for loading dynamic libraries
this is going to only work for very basic libraries; I plan to slowly add more features over time to support more complicated libraries
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/codegen.cpp12
-rw-r--r--src/link.cpp4
-rw-r--r--std/dynamic_library.zig161
-rw-r--r--std/elf.zig15
-rw-r--r--std/index.zig1
-rw-r--r--std/io.zig7
-rw-r--r--std/math/index.zig11
-rw-r--r--std/os/file.zig11
-rw-r--r--std/os/index.zig14
-rw-r--r--test/build_examples.zig5
-rw-r--r--test/standalone/load_dynamic_library/add.zig3
-rw-r--r--test/standalone/load_dynamic_library/build.zig22
-rw-r--r--test/standalone/load_dynamic_library/main.zig17
14 files changed, 265 insertions, 19 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dd4770ad72..e502901bd2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -438,6 +438,7 @@ set(ZIG_STD_FILES
"debug/failing_allocator.zig"
"debug/index.zig"
"dwarf.zig"
+ "dynamic_library.zig"
"elf.zig"
"empty.zig"
"event.zig"
diff --git a/src/codegen.cpp b/src/codegen.cpp
index d05bcba2ce..fedfcfa744 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -6768,7 +6768,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
int err;
Buf *abs_full_path = buf_alloc();
if ((err = os_path_real(builtin_zig_path, abs_full_path))) {
- fprintf(stderr, "unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err));
+ fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err));
exit(1);
}
@@ -6936,11 +6936,11 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package
Buf *abs_full_path = buf_alloc();
int err;
if ((err = os_path_real(&path_to_code_src, abs_full_path))) {
- zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err));
+ zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err));
}
Buf *import_code = buf_alloc();
if ((err = os_fetch_file_path(abs_full_path, import_code, false))) {
- zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err));
+ zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err));
}
return add_source_file(g, package, abs_full_path, import_code);
@@ -7024,13 +7024,13 @@ static void gen_root_source(CodeGen *g) {
Buf *abs_full_path = buf_alloc();
int err;
if ((err = os_path_real(rel_full_path, abs_full_path))) {
- fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
+ fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(rel_full_path), err_str(err));
exit(1);
}
Buf *source_code = buf_alloc();
if ((err = os_fetch_file_path(rel_full_path, source_code, true))) {
- fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
+ fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(rel_full_path), err_str(err));
exit(1);
}
@@ -7374,7 +7374,7 @@ static void gen_h_file(CodeGen *g) {
FILE *out_h = fopen(buf_ptr(g->out_h_path), "wb");
if (!out_h)
- zig_panic("unable to open %s: %s", buf_ptr(g->out_h_path), strerror(errno));
+ zig_panic("unable to open %s: %s\n", buf_ptr(g->out_h_path), strerror(errno));
Buf *export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name)));
buf_upcase(export_macro);
diff --git a/src/link.cpp b/src/link.cpp
index d2925cb5a8..a4631b1daf 100644
--- a/src/link.cpp
+++ b/src/link.cpp
@@ -208,7 +208,7 @@ static Buf *get_dynamic_linker_path(CodeGen *g) {
static void construct_linker_job_elf(LinkJob *lj) {
CodeGen *g = lj->codegen;
- if (lj->link_in_crt) {
+ if (g->libc_link_lib != nullptr) {
find_libc_lib_path(g);
}
@@ -432,7 +432,7 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si
static void construct_linker_job_coff(LinkJob *lj) {
CodeGen *g = lj->codegen;
- if (lj->link_in_crt) {
+ if (g->libc_link_lib != nullptr) {
find_libc_lib_path(g);
}
diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig
new file mode 100644
index 0000000000..87b58ec207
--- /dev/null
+++ b/std/dynamic_library.zig
@@ -0,0 +1,161 @@
+const std = @import("index.zig");
+const mem = std.mem;
+const elf = std.elf;
+const cstr = std.cstr;
+const linux = std.os.linux;
+
+pub const DynLib = struct {
+ allocator: *mem.Allocator,
+ elf_lib: ElfLib,
+ fd: i32,
+ map_addr: usize,
+ map_size: usize,
+
+ /// Trusts the file
+ pub fn findAndOpen(allocator: *mem.Allocator, name: []const u8) !DynLib {
+ return open(allocator, name);
+ }
+
+ /// Trusts the file
+ pub fn open(allocator: *mem.Allocator, path: []const u8) !DynLib {
+ const fd = try std.os.posixOpen(allocator, path, 0, linux.O_RDONLY);
+ errdefer std.os.close(fd);
+
+ const size = usize((try std.os.posixFStat(fd)).size);
+
+ const addr = linux.mmap(
+ null,
+ size,
+ linux.PROT_READ | linux.PROT_EXEC,
+ linux.MAP_PRIVATE | linux.MAP_LOCKED,
+ fd,
+ 0,
+ );
+ errdefer _ = linux.munmap(addr, size);
+
+ const bytes = @intToPtr([*]align(std.os.page_size) u8, addr)[0..size];
+
+ return DynLib{
+ .allocator = allocator,
+ .elf_lib = try ElfLib.init(bytes),
+ .fd = fd,
+ .map_addr = addr,
+ .map_size = size,
+ };
+ }
+
+ pub fn close(self: *DynLib) void {
+ _ = linux.munmap(self.map_addr, self.map_size);
+ std.os.close(self.fd);
+ self.* = undefined;
+ }
+
+ pub fn lookup(self: *DynLib, name: []const u8) ?usize {
+ return self.elf_lib.lookup("", name);
+ }
+};
+
+pub const ElfLib = struct {
+ strings: [*]u8,
+ syms: [*]elf.Sym,
+ hashtab: [*]linux.Elf_Symndx,
+ versym: ?[*]u16,
+ verdef: ?*elf.Verdef,
+ base: usize,
+
+ // Trusts the memory
+ pub fn init(bytes: []align(@alignOf(elf.Ehdr)) u8) !ElfLib {
+ const eh = @ptrCast(*elf.Ehdr, bytes.ptr);
+ if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile;
+ if (eh.e_type != elf.ET_DYN) return error.NotDynamicLibrary;
+
+ const elf_addr = @ptrToInt(bytes.ptr);
+ var ph_addr: usize = elf_addr + eh.e_phoff;
+
+ var base: usize = @maxValue(usize);
+ var maybe_dynv: ?[*]usize = null;
+ {
+ var i: usize = 0;
+ while (i < eh.e_phnum) : ({
+ i += 1;
+ ph_addr += eh.e_phentsize;
+ }) {
+ const ph = @intToPtr(*elf.Phdr, ph_addr);
+ switch (ph.p_type) {
+ elf.PT_LOAD => base = elf_addr + ph.p_offset - ph.p_vaddr,
+ elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, elf_addr + ph.p_offset),
+ else => {},
+ }
+ }
+ }
+ const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation;
+ if (base == @maxValue(usize)) return error.BaseNotFound;
+
+ var maybe_strings: ?[*]u8 = null;
+ var maybe_syms: ?[*]elf.Sym = null;
+ var maybe_hashtab: ?[*]linux.Elf_Symndx = null;
+ var maybe_versym: ?[*]u16 = null;
+ var maybe_verdef: ?*elf.Verdef = null;
+
+ {
+ var i: usize = 0;
+ while (dynv[i] != 0) : (i += 2) {
+ const p = base + dynv[i + 1];
+ switch (dynv[i]) {
+ elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p),
+ elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p),
+ elf.DT_HASH => maybe_hashtab = @intToPtr([*]linux.Elf_Symndx, p),
+ elf.DT_VERSYM => maybe_versym = @intToPtr([*]u16, p),
+ elf.DT_VERDEF => maybe_verdef = @intToPtr(*elf.Verdef, p),
+ else => {},
+ }
+ }
+ }
+
+ return ElfLib{
+ .base = base,
+ .strings = maybe_strings orelse return error.ElfStringSectionNotFound,
+ .syms = maybe_syms orelse return error.ElfSymSectionNotFound,
+ .hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound,
+ .versym = maybe_versym,
+ .verdef = maybe_verdef,
+ };
+ }
+
+ /// Returns the address of the symbol
+ pub fn lookup(self: *const ElfLib, vername: []const u8, name: []const u8) ?usize {
+ const maybe_versym = if (self.verdef == null) null else self.versym;
+
+ const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON);
+ const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE);
+
+ var i: usize = 0;
+ while (i < self.hashtab[1]) : (i += 1) {
+ if (0 == (u32(1) << u5(self.syms[i].st_info & 0xf) & OK_TYPES)) continue;
+ if (0 == (u32(1) << u5(self.syms[i].st_info >> 4) & OK_BINDS)) continue;
+ if (0 == self.syms[i].st_shndx) continue;
+ if (!mem.eql(u8, name, cstr.toSliceConst(self.strings + self.syms[i].st_name))) continue;
+ if (maybe_versym) |versym| {
+ if (!checkver(self.verdef.?, versym[i], vername, self.strings))
+ continue;
+ }
+ return self.base + self.syms[i].st_value;
+ }
+
+ return null;
+ }
+};
+
+fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*]u8) bool {
+ var def = def_arg;
+ const vsym = @bitCast(u32, vsym_arg) & 0x7fff;
+ while (true) {
+ if (0 == (def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym)
+ break;
+ if (def.vd_next == 0)
+ return false;
+ def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next);
+ }
+ const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux);
+ return mem.eql(u8, vername, cstr.toSliceConst(strings + aux.vda_name));
+}
diff --git a/std/elf.zig b/std/elf.zig
index 50e97ab271..8e6445c631 100644
--- a/std/elf.zig
+++ b/std/elf.zig
@@ -305,6 +305,21 @@ pub const STT_ARM_16BIT = STT_HIPROC;
pub const VER_FLG_BASE = 0x1;
pub const VER_FLG_WEAK = 0x2;
+/// An unknown type.
+pub const ET_NONE = 0;
+
+/// A relocatable file.
+pub const ET_REL = 1;
+
+/// An executable file.
+pub const ET_EXEC = 2;
+
+/// A shared object.
+pub const ET_DYN = 3;
+
+/// A core file.
+pub const ET_CORE = 4;
+
pub const FileType = enum {
Relocatable,
Executable,
diff --git a/std/index.zig b/std/index.zig
index 8abfa3db88..3b523f519f 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap;
pub const LinkedList = @import("linked_list.zig").LinkedList;
pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
+pub const DynLib = @import("dynamic_library.zig").DynLib;
pub const atomic = @import("atomic/index.zig");
pub const base64 = @import("base64.zig");
diff --git a/std/io.zig b/std/io.zig
index a603d0cf5e..cfe1a7f585 100644
--- a/std/io.zig
+++ b/std/io.zig
@@ -242,11 +242,16 @@ pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8)
/// On success, caller owns returned buffer.
pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
+ return readFileAllocAligned(allocator, path, @alignOf(u8));
+}
+
+/// On success, caller owns returned buffer.
+pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {
var file = try File.openRead(allocator, path);
defer file.close();
const size = try file.getEndPos();
- const buf = try allocator.alloc(u8, size);
+ const buf = try allocator.alignedAlloc(u8, A, size);
errdefer allocator.free(buf);
var adapter = FileInStream.init(&file);
diff --git a/std/math/index.zig b/std/math/index.zig
index cc1b833a37..8c1dcc32c4 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -536,6 +536,17 @@ test "math.cast" {
assert(@typeOf(try cast(u8, u32(255))) == u8);
}
+pub const AlignCastError = error{UnalignedMemory};
+
+/// Align cast a pointer but return an error if it's the wrong field
+pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alignCast(alignment, ptr)) {
+ const addr = @ptrToInt(ptr);
+ if (addr % alignment != 0) {
+ return error.UnalignedMemory;
+ }
+ return @alignCast(alignment, ptr);
+}
+
pub fn floorPowerOfTwo(comptime T: type, value: T) T {
var x = value;
diff --git a/std/os/file.zig b/std/os/file.zig
index 56da4f73a6..41d3dfbf95 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -265,16 +265,7 @@ pub const File = struct {
pub fn getEndPos(self: *File) !usize {
if (is_posix) {
- var stat: posix.Stat = undefined;
- const err = posix.getErrno(posix.fstat(self.handle, &stat));
- if (err > 0) {
- return switch (err) {
- posix.EBADF => error.BadFd,
- posix.ENOMEM => error.SystemResources,
- else => os.unexpectedErrorPosix(err),
- };
- }
-
+ const stat = try os.posixFStat(self.handle);
return usize(stat.size);
} else if (is_windows) {
var file_size: windows.LARGE_INTEGER = undefined;
diff --git a/std/os/index.zig b/std/os/index.zig
index 62eeb7e43e..fb4605fce0 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2697,3 +2697,17 @@ pub fn posixWait(pid: i32) i32 {
}
}
}
+
+pub fn posixFStat(fd: i32) !posix.Stat {
+ var stat: posix.Stat = undefined;
+ const err = posix.getErrno(posix.fstat(fd, &stat));
+ if (err > 0) {
+ return switch (err) {
+ posix.EBADF => error.BadFd,
+ posix.ENOMEM => error.SystemResources,
+ else => os.unexpectedErrorPosix(err),
+ };
+ }
+
+ return stat;
+}
diff --git a/test/build_examples.zig b/test/build_examples.zig
index 1ba0ca46cf..7cae734677 100644
--- a/test/build_examples.zig
+++ b/test/build_examples.zig
@@ -18,4 +18,9 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void {
cases.addBuildFile("test/standalone/pkg_import/build.zig");
cases.addBuildFile("test/standalone/use_alias/build.zig");
cases.addBuildFile("test/standalone/brace_expansion/build.zig");
+ if (builtin.os == builtin.Os.linux) {
+ // TODO hook up the DynLib API for windows using LoadLibraryA
+ // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it
+ cases.addBuildFile("test/standalone/load_dynamic_library/build.zig");
+ }
}
diff --git a/test/standalone/load_dynamic_library/add.zig b/test/standalone/load_dynamic_library/add.zig
new file mode 100644
index 0000000000..a04ec1544d
--- /dev/null
+++ b/test/standalone/load_dynamic_library/add.zig
@@ -0,0 +1,3 @@
+export fn add(a: i32, b: i32) i32 {
+ return a + b;
+}
diff --git a/test/standalone/load_dynamic_library/build.zig b/test/standalone/load_dynamic_library/build.zig
new file mode 100644
index 0000000000..1f981a5c7d
--- /dev/null
+++ b/test/standalone/load_dynamic_library/build.zig
@@ -0,0 +1,22 @@
+const Builder = @import("std").build.Builder;
+
+pub fn build(b: *Builder) void {
+ const opts = b.standardReleaseOptions();
+
+ const lib = b.addSharedLibrary("add", "add.zig", b.version(1, 0, 0));
+ lib.setBuildMode(opts);
+ lib.linkSystemLibrary("c");
+
+ const main = b.addExecutable("main", "main.zig");
+ main.setBuildMode(opts);
+
+ const run = b.addCommand(".", b.env_map, [][]const u8{
+ main.getOutputPath(),
+ lib.getOutputPath(),
+ });
+ run.step.dependOn(&lib.step);
+ run.step.dependOn(&main.step);
+
+ const test_step = b.step("test", "Test the program");
+ test_step.dependOn(&run.step);
+}
diff --git a/test/standalone/load_dynamic_library/main.zig b/test/standalone/load_dynamic_library/main.zig
new file mode 100644
index 0000000000..4c45ad6fde
--- /dev/null
+++ b/test/standalone/load_dynamic_library/main.zig
@@ -0,0 +1,17 @@
+const std = @import("std");
+
+pub fn main() !void {
+ const args = try std.os.argsAlloc(std.debug.global_allocator);
+ defer std.os.argsFree(std.debug.global_allocator, args);
+
+ const dynlib_name = args[1];
+
+ var lib = try std.DynLib.open(std.debug.global_allocator, dynlib_name);
+ defer lib.close();
+
+ const addr = lib.lookup("add") orelse return error.SymbolNotFound;
+ const addFn = @intToPtr(extern fn (i32, i32) i32, addr);
+
+ const result = addFn(12, 34);
+ std.debug.assert(result == 46);
+}