| author | |
| committer | |
| log | 48de57d8248d9203b44d28d7749b5d7c1a00deba |
| tree | 149fb86d959501ecb2e5e2aac7f688265f729ba6 |
| parent | b3a3e2094e8b88e40eecf65d29f39af10442bde4 |
this is going to only work for very basic libraries;
I plan to slowly add more features over time to support more
complicated libraries14 files changed, 265 insertions(+), 19 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -438,6 +438,7 @@ set(ZIG_STD_FILES |
| 438 | 438 | "debug/failing_allocator.zig" |
| 439 | 439 | "debug/index.zig" |
| 440 | 440 | "dwarf.zig" |
| 441 | "dynamic_library.zig" | |
| 441 | 442 | "elf.zig" |
| 442 | 443 | "empty.zig" |
| 443 | 444 | "event.zig" |
src/codegen.cpp+6-6| ... | ... | @@ -6768,7 +6768,7 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 6768 | 6768 | int err; |
| 6769 | 6769 | Buf *abs_full_path = buf_alloc(); |
| 6770 | 6770 | 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)); | |
| 6772 | 6772 | exit(1); |
| 6773 | 6773 | } |
| 6774 | 6774 | |
| ... | ... | @@ -6936,11 +6936,11 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package |
| 6936 | 6936 | Buf *abs_full_path = buf_alloc(); |
| 6937 | 6937 | int err; |
| 6938 | 6938 | 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)); | |
| 6940 | 6940 | } |
| 6941 | 6941 | Buf *import_code = buf_alloc(); |
| 6942 | 6942 | 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)); | |
| 6944 | 6944 | } |
| 6945 | 6945 | |
| 6946 | 6946 | return add_source_file(g, package, abs_full_path, import_code); |
| ... | ... | @@ -7024,13 +7024,13 @@ static void gen_root_source(CodeGen *g) { |
| 7024 | 7024 | Buf *abs_full_path = buf_alloc(); |
| 7025 | 7025 | int err; |
| 7026 | 7026 | 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)); | |
| 7028 | 7028 | exit(1); |
| 7029 | 7029 | } |
| 7030 | 7030 | |
| 7031 | 7031 | Buf *source_code = buf_alloc(); |
| 7032 | 7032 | 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)); | |
| 7034 | 7034 | exit(1); |
| 7035 | 7035 | } |
| 7036 | 7036 | |
| ... | ... | @@ -7374,7 +7374,7 @@ static void gen_h_file(CodeGen *g) { |
| 7374 | 7374 | |
| 7375 | 7375 | FILE *out_h = fopen(buf_ptr(g->out_h_path), "wb"); |
| 7376 | 7376 | 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)); | |
| 7378 | 7378 | |
| 7379 | 7379 | Buf *export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name))); |
| 7380 | 7380 | buf_upcase(export_macro); |
src/link.cpp+2-2| ... | ... | @@ -208,7 +208,7 @@ static Buf *get_dynamic_linker_path(CodeGen *g) { |
| 208 | 208 | static void construct_linker_job_elf(LinkJob *lj) { |
| 209 | 209 | CodeGen *g = lj->codegen; |
| 210 | 210 | |
| 211 | if (lj->link_in_crt) { | |
| 211 | if (g->libc_link_lib != nullptr) { | |
| 212 | 212 | find_libc_lib_path(g); |
| 213 | 213 | } |
| 214 | 214 | |
| ... | ... | @@ -432,7 +432,7 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si |
| 432 | 432 | static void construct_linker_job_coff(LinkJob *lj) { |
| 433 | 433 | CodeGen *g = lj->codegen; |
| 434 | 434 | |
| 435 | if (lj->link_in_crt) { | |
| 435 | if (g->libc_link_lib != nullptr) { | |
| 436 | 436 | find_libc_lib_path(g); |
| 437 | 437 | } |
| 438 | 438 |
std/dynamic_library.zig created+161| ... | ... | @@ -0,0 +1,161 @@ |
| 1 | const std = @import("index.zig"); | |
| 2 | const mem = std.mem; | |
| 3 | const elf = std.elf; | |
| 4 | const cstr = std.cstr; | |
| 5 | const linux = std.os.linux; | |
| 6 | ||
| 7 | pub 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 | ||
| 58 | pub 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 | ||
| 149 | fn 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; |
| 305 | 305 | pub const VER_FLG_BASE = 0x1; |
| 306 | 306 | pub const VER_FLG_WEAK = 0x2; |
| 307 | 307 | |
| 308 | /// An unknown type. | |
| 309 | pub const ET_NONE = 0; | |
| 310 | ||
| 311 | /// A relocatable file. | |
| 312 | pub const ET_REL = 1; | |
| 313 | ||
| 314 | /// An executable file. | |
| 315 | pub const ET_EXEC = 2; | |
| 316 | ||
| 317 | /// A shared object. | |
| 318 | pub const ET_DYN = 3; | |
| 319 | ||
| 320 | /// A core file. | |
| 321 | pub const ET_CORE = 4; | |
| 322 | ||
| 308 | 323 | pub const FileType = enum { |
| 309 | 324 | Relocatable, |
| 310 | 325 | Executable, |
std/index.zig+1| ... | ... | @@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap; |
| 8 | 8 | pub const LinkedList = @import("linked_list.zig").LinkedList; |
| 9 | 9 | pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; |
| 10 | 10 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; |
| 11 | pub const DynLib = @import("dynamic_library.zig").DynLib; | |
| 11 | 12 | |
| 12 | 13 | pub const atomic = @import("atomic/index.zig"); |
| 13 | 14 | pub 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) |
| 242 | 242 | |
| 243 | 243 | /// On success, caller owns returned buffer. |
| 244 | 244 | pub 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. | |
| 249 | pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 { | |
| 245 | 250 | var file = try File.openRead(allocator, path); |
| 246 | 251 | defer file.close(); |
| 247 | 252 | |
| 248 | 253 | const size = try file.getEndPos(); |
| 249 | const buf = try allocator.alloc(u8, size); | |
| 254 | const buf = try allocator.alignedAlloc(u8, A, size); | |
| 250 | 255 | errdefer allocator.free(buf); |
| 251 | 256 | |
| 252 | 257 | var adapter = FileInStream.init(&file); |
std/math/index.zig+11| ... | ... | @@ -536,6 +536,17 @@ test "math.cast" { |
| 536 | 536 | assert(@typeOf(try cast(u8, u32(255))) == u8); |
| 537 | 537 | } |
| 538 | 538 | |
| 539 | pub const AlignCastError = error{UnalignedMemory}; | |
| 540 | ||
| 541 | /// Align cast a pointer but return an error if it's the wrong field | |
| 542 | pub 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 | ||
| 539 | 550 | pub fn floorPowerOfTwo(comptime T: type, value: T) T { |
| 540 | 551 | var x = value; |
| 541 | 552 |
std/os/file.zig+1-10| ... | ... | @@ -265,16 +265,7 @@ pub const File = struct { |
| 265 | 265 | |
| 266 | 266 | pub fn getEndPos(self: *File) !usize { |
| 267 | 267 | 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); | |
| 278 | 269 | return usize(stat.size); |
| 279 | 270 | } else if (is_windows) { |
| 280 | 271 | var file_size: windows.LARGE_INTEGER = undefined; |
std/os/index.zig+14| ... | ... | @@ -2697,3 +2697,17 @@ pub fn posixWait(pid: i32) i32 { |
| 2697 | 2697 | } |
| 2698 | 2698 | } |
| 2699 | 2699 | } |
| 2700 | ||
| 2701 | pub 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 { |
| 18 | 18 | cases.addBuildFile("test/standalone/pkg_import/build.zig"); |
| 19 | 19 | cases.addBuildFile("test/standalone/use_alias/build.zig"); |
| 20 | 20 | 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 | } | |
| 21 | 26 | } |
test/standalone/load_dynamic_library/add.zig created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | export 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 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub 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 | } |