authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-16 17:01:23-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-16 17:01:23-04:00
log48de57d8248d9203b44d28d7749b5d7c1a00deba
tree149fb86d959501ecb2e5e2aac7f688265f729ba6
parentb3a3e2094e8b88e40eecf65d29f39af10442bde4

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

14 files changed, 265 insertions(+), 19 deletions(-)

CMakeLists.txt+1
......@@ -438,6 +438,7 @@ set(ZIG_STD_FILES
438438 "debug/failing_allocator.zig"
439439 "debug/index.zig"
440440 "dwarf.zig"
441 "dynamic_library.zig"
441442 "elf.zig"
442443 "empty.zig"
443444 "event.zig"
src/codegen.cpp+6-6
......@@ -6768,7 +6768,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
67686768 int err;
67696769 Buf *abs_full_path = buf_alloc();
67706770 if ((err = os_path_real(builtin_zig_path, abs_full_path))) {
6771 fprintf(stderr, "unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err));
6771 fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err));
67726772 exit(1);
67736773 }
67746774
......@@ -6936,11 +6936,11 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package
69366936 Buf *abs_full_path = buf_alloc();
69376937 int err;
69386938 if ((err = os_path_real(&path_to_code_src, abs_full_path))) {
6939 zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err));
6939 zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err));
69406940 }
69416941 Buf *import_code = buf_alloc();
69426942 if ((err = os_fetch_file_path(abs_full_path, import_code, false))) {
6943 zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err));
6943 zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err));
69446944 }
69456945
69466946 return add_source_file(g, package, abs_full_path, import_code);
......@@ -7024,13 +7024,13 @@ static void gen_root_source(CodeGen *g) {
70247024 Buf *abs_full_path = buf_alloc();
70257025 int err;
70267026 if ((err = os_path_real(rel_full_path, abs_full_path))) {
7027 fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
7027 fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(rel_full_path), err_str(err));
70287028 exit(1);
70297029 }
70307030
70317031 Buf *source_code = buf_alloc();
70327032 if ((err = os_fetch_file_path(rel_full_path, source_code, true))) {
7033 fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
7033 fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(rel_full_path), err_str(err));
70347034 exit(1);
70357035 }
70367036
......@@ -7374,7 +7374,7 @@ static void gen_h_file(CodeGen *g) {
73747374
73757375 FILE *out_h = fopen(buf_ptr(g->out_h_path), "wb");
73767376 if (!out_h)
7377 zig_panic("unable to open %s: %s", buf_ptr(g->out_h_path), strerror(errno));
7377 zig_panic("unable to open %s: %s\n", buf_ptr(g->out_h_path), strerror(errno));
73787378
73797379 Buf *export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name)));
73807380 buf_upcase(export_macro);
src/link.cpp+2-2
......@@ -208,7 +208,7 @@ static Buf *get_dynamic_linker_path(CodeGen *g) {
208208static void construct_linker_job_elf(LinkJob *lj) {
209209 CodeGen *g = lj->codegen;
210210
211 if (lj->link_in_crt) {
211 if (g->libc_link_lib != nullptr) {
212212 find_libc_lib_path(g);
213213 }
214214
......@@ -432,7 +432,7 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si
432432static void construct_linker_job_coff(LinkJob *lj) {
433433 CodeGen *g = lj->codegen;
434434
435 if (lj->link_in_crt) {
435 if (g->libc_link_lib != nullptr) {
436436 find_libc_lib_path(g);
437437 }
438438
std/dynamic_library.zig created+161
......@@ -0,0 +1,161 @@
1const std = @import("index.zig");
2const mem = std.mem;
3const elf = std.elf;
4const cstr = std.cstr;
5const linux = std.os.linux;
6
7pub const DynLib = struct {
8 allocator: *mem.Allocator,
9 elf_lib: ElfLib,
10 fd: i32,
11 map_addr: usize,
12 map_size: usize,
13
14 /// Trusts the file
15 pub fn findAndOpen(allocator: *mem.Allocator, name: []const u8) !DynLib {
16 return open(allocator, name);
17 }
18
19 /// Trusts the file
20 pub fn open(allocator: *mem.Allocator, path: []const u8) !DynLib {
21 const fd = try std.os.posixOpen(allocator, path, 0, linux.O_RDONLY);
22 errdefer std.os.close(fd);
23
24 const size = usize((try std.os.posixFStat(fd)).size);
25
26 const addr = linux.mmap(
27 null,
28 size,
29 linux.PROT_READ | linux.PROT_EXEC,
30 linux.MAP_PRIVATE | linux.MAP_LOCKED,
31 fd,
32 0,
33 );
34 errdefer _ = linux.munmap(addr, size);
35
36 const bytes = @intToPtr([*]align(std.os.page_size) u8, addr)[0..size];
37
38 return DynLib{
39 .allocator = allocator,
40 .elf_lib = try ElfLib.init(bytes),
41 .fd = fd,
42 .map_addr = addr,
43 .map_size = size,
44 };
45 }
46
47 pub fn close(self: *DynLib) void {
48 _ = linux.munmap(self.map_addr, self.map_size);
49 std.os.close(self.fd);
50 self.* = undefined;
51 }
52
53 pub fn lookup(self: *DynLib, name: []const u8) ?usize {
54 return self.elf_lib.lookup("", name);
55 }
56};
57
58pub const ElfLib = struct {
59 strings: [*]u8,
60 syms: [*]elf.Sym,
61 hashtab: [*]linux.Elf_Symndx,
62 versym: ?[*]u16,
63 verdef: ?*elf.Verdef,
64 base: usize,
65
66 // Trusts the memory
67 pub fn init(bytes: []align(@alignOf(elf.Ehdr)) u8) !ElfLib {
68 const eh = @ptrCast(*elf.Ehdr, bytes.ptr);
69 if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile;
70 if (eh.e_type != elf.ET_DYN) return error.NotDynamicLibrary;
71
72 const elf_addr = @ptrToInt(bytes.ptr);
73 var ph_addr: usize = elf_addr + eh.e_phoff;
74
75 var base: usize = @maxValue(usize);
76 var maybe_dynv: ?[*]usize = null;
77 {
78 var i: usize = 0;
79 while (i < eh.e_phnum) : ({
80 i += 1;
81 ph_addr += eh.e_phentsize;
82 }) {
83 const ph = @intToPtr(*elf.Phdr, ph_addr);
84 switch (ph.p_type) {
85 elf.PT_LOAD => base = elf_addr + ph.p_offset - ph.p_vaddr,
86 elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, elf_addr + ph.p_offset),
87 else => {},
88 }
89 }
90 }
91 const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation;
92 if (base == @maxValue(usize)) return error.BaseNotFound;
93
94 var maybe_strings: ?[*]u8 = null;
95 var maybe_syms: ?[*]elf.Sym = null;
96 var maybe_hashtab: ?[*]linux.Elf_Symndx = null;
97 var maybe_versym: ?[*]u16 = null;
98 var maybe_verdef: ?*elf.Verdef = null;
99
100 {
101 var i: usize = 0;
102 while (dynv[i] != 0) : (i += 2) {
103 const p = base + dynv[i + 1];
104 switch (dynv[i]) {
105 elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p),
106 elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p),
107 elf.DT_HASH => maybe_hashtab = @intToPtr([*]linux.Elf_Symndx, p),
108 elf.DT_VERSYM => maybe_versym = @intToPtr([*]u16, p),
109 elf.DT_VERDEF => maybe_verdef = @intToPtr(*elf.Verdef, p),
110 else => {},
111 }
112 }
113 }
114
115 return ElfLib{
116 .base = base,
117 .strings = maybe_strings orelse return error.ElfStringSectionNotFound,
118 .syms = maybe_syms orelse return error.ElfSymSectionNotFound,
119 .hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound,
120 .versym = maybe_versym,
121 .verdef = maybe_verdef,
122 };
123 }
124
125 /// Returns the address of the symbol
126 pub fn lookup(self: *const ElfLib, vername: []const u8, name: []const u8) ?usize {
127 const maybe_versym = if (self.verdef == null) null else self.versym;
128
129 const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON);
130 const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE);
131
132 var i: usize = 0;
133 while (i < self.hashtab[1]) : (i += 1) {
134 if (0 == (u32(1) << u5(self.syms[i].st_info & 0xf) & OK_TYPES)) continue;
135 if (0 == (u32(1) << u5(self.syms[i].st_info >> 4) & OK_BINDS)) continue;
136 if (0 == self.syms[i].st_shndx) continue;
137 if (!mem.eql(u8, name, cstr.toSliceConst(self.strings + self.syms[i].st_name))) continue;
138 if (maybe_versym) |versym| {
139 if (!checkver(self.verdef.?, versym[i], vername, self.strings))
140 continue;
141 }
142 return self.base + self.syms[i].st_value;
143 }
144
145 return null;
146 }
147};
148
149fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*]u8) bool {
150 var def = def_arg;
151 const vsym = @bitCast(u32, vsym_arg) & 0x7fff;
152 while (true) {
153 if (0 == (def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym)
154 break;
155 if (def.vd_next == 0)
156 return false;
157 def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next);
158 }
159 const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux);
160 return mem.eql(u8, vername, cstr.toSliceConst(strings + aux.vda_name));
161}
std/elf.zig+15
......@@ -305,6 +305,21 @@ pub const STT_ARM_16BIT = STT_HIPROC;
305305pub const VER_FLG_BASE = 0x1;
306306pub const VER_FLG_WEAK = 0x2;
307307
308/// An unknown type.
309pub const ET_NONE = 0;
310
311/// A relocatable file.
312pub const ET_REL = 1;
313
314/// An executable file.
315pub const ET_EXEC = 2;
316
317/// A shared object.
318pub const ET_DYN = 3;
319
320/// A core file.
321pub const ET_CORE = 4;
322
308323pub const FileType = enum {
309324 Relocatable,
310325 Executable,
std/index.zig+1
......@@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap;
88pub const LinkedList = @import("linked_list.zig").LinkedList;
99pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
1010pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
11pub const DynLib = @import("dynamic_library.zig").DynLib;
1112
1213pub const atomic = @import("atomic/index.zig");
1314pub const base64 = @import("base64.zig");
std/io.zig+6-1
......@@ -242,11 +242,16 @@ pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8)
242242
243243/// On success, caller owns returned buffer.
244244pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
245 return readFileAllocAligned(allocator, path, @alignOf(u8));
246}
247
248/// On success, caller owns returned buffer.
249pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {
245250 var file = try File.openRead(allocator, path);
246251 defer file.close();
247252
248253 const size = try file.getEndPos();
249 const buf = try allocator.alloc(u8, size);
254 const buf = try allocator.alignedAlloc(u8, A, size);
250255 errdefer allocator.free(buf);
251256
252257 var adapter = FileInStream.init(&file);
std/math/index.zig+11
......@@ -536,6 +536,17 @@ test "math.cast" {
536536 assert(@typeOf(try cast(u8, u32(255))) == u8);
537537}
538538
539pub const AlignCastError = error{UnalignedMemory};
540
541/// Align cast a pointer but return an error if it's the wrong field
542pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alignCast(alignment, ptr)) {
543 const addr = @ptrToInt(ptr);
544 if (addr % alignment != 0) {
545 return error.UnalignedMemory;
546 }
547 return @alignCast(alignment, ptr);
548}
549
539550pub fn floorPowerOfTwo(comptime T: type, value: T) T {
540551 var x = value;
541552
std/os/file.zig+1-10
......@@ -265,16 +265,7 @@ pub const File = struct {
265265
266266 pub fn getEndPos(self: *File) !usize {
267267 if (is_posix) {
268 var stat: posix.Stat = undefined;
269 const err = posix.getErrno(posix.fstat(self.handle, &stat));
270 if (err > 0) {
271 return switch (err) {
272 posix.EBADF => error.BadFd,
273 posix.ENOMEM => error.SystemResources,
274 else => os.unexpectedErrorPosix(err),
275 };
276 }
277
268 const stat = try os.posixFStat(self.handle);
278269 return usize(stat.size);
279270 } else if (is_windows) {
280271 var file_size: windows.LARGE_INTEGER = undefined;
std/os/index.zig+14
......@@ -2697,3 +2697,17 @@ pub fn posixWait(pid: i32) i32 {
26972697 }
26982698 }
26992699}
2700
2701pub fn posixFStat(fd: i32) !posix.Stat {
2702 var stat: posix.Stat = undefined;
2703 const err = posix.getErrno(posix.fstat(fd, &stat));
2704 if (err > 0) {
2705 return switch (err) {
2706 posix.EBADF => error.BadFd,
2707 posix.ENOMEM => error.SystemResources,
2708 else => os.unexpectedErrorPosix(err),
2709 };
2710 }
2711
2712 return stat;
2713}
test/build_examples.zig+5
......@@ -18,4 +18,9 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void {
1818 cases.addBuildFile("test/standalone/pkg_import/build.zig");
1919 cases.addBuildFile("test/standalone/use_alias/build.zig");
2020 cases.addBuildFile("test/standalone/brace_expansion/build.zig");
21 if (builtin.os == builtin.Os.linux) {
22 // TODO hook up the DynLib API for windows using LoadLibraryA
23 // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it
24 cases.addBuildFile("test/standalone/load_dynamic_library/build.zig");
25 }
2126}
test/standalone/load_dynamic_library/add.zig created+3
......@@ -0,0 +1,3 @@
1export fn add(a: i32, b: i32) i32 {
2 return a + b;
3}
test/standalone/load_dynamic_library/build.zig created+22
......@@ -0,0 +1,22 @@
1const Builder = @import("std").build.Builder;
2
3pub fn build(b: *Builder) void {
4 const opts = b.standardReleaseOptions();
5
6 const lib = b.addSharedLibrary("add", "add.zig", b.version(1, 0, 0));
7 lib.setBuildMode(opts);
8 lib.linkSystemLibrary("c");
9
10 const main = b.addExecutable("main", "main.zig");
11 main.setBuildMode(opts);
12
13 const run = b.addCommand(".", b.env_map, [][]const u8{
14 main.getOutputPath(),
15 lib.getOutputPath(),
16 });
17 run.step.dependOn(&lib.step);
18 run.step.dependOn(&main.step);
19
20 const test_step = b.step("test", "Test the program");
21 test_step.dependOn(&run.step);
22}
test/standalone/load_dynamic_library/main.zig created+17
......@@ -0,0 +1,17 @@
1const std = @import("std");
2
3pub fn main() !void {
4 const args = try std.os.argsAlloc(std.debug.global_allocator);
5 defer std.os.argsFree(std.debug.global_allocator, args);
6
7 const dynlib_name = args[1];
8
9 var lib = try std.DynLib.open(std.debug.global_allocator, dynlib_name);
10 defer lib.close();
11
12 const addr = lib.lookup("add") orelse return error.SymbolNotFound;
13 const addFn = @intToPtr(extern fn (i32, i32) i32, addr);
14
15 const result = addFn(12, 34);
16 std.debug.assert(result == 46);
17}