Skip to content

Commit 48de57d

Browse files
committedJun 16, 2018
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
·
0.15.20.3.0
1 parent b3a3e20 commit 48de57d

File tree

14 files changed

+265
-19
lines changed

14 files changed

+265
-19
lines changed
 

‎CMakeLists.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ static Buf *get_dynamic_linker_path(CodeGen *g) {
208208
static 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
432432
static 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‎

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,21 @@ pub const STT_ARM_16BIT = STT_HIPROC;
305305
pub const VER_FLG_BASE = 0x1;
306306
pub const VER_FLG_WEAK = 0x2;
307307

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+
308323
pub const FileType = enum {
309324
Relocatable,
310325
Executable,

‎std/index.zig‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap;
88
pub const LinkedList = @import("linked_list.zig").LinkedList;
99
pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
1010
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
11+
pub const DynLib = @import("dynamic_library.zig").DynLib;
1112

1213
pub const atomic = @import("atomic/index.zig");
1314
pub const base64 = @import("base64.zig");

‎std/io.zig‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,16 @@ pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8)
242242

243243
/// On success, caller owns returned buffer.
244244
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 {
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‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,17 @@ test "math.cast" {
536536
assert(@typeOf(try cast(u8, u32(255))) == u8);
537537
}
538538

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+
539550
pub fn floorPowerOfTwo(comptime T: type, value: T) T {
540551
var x = value;
541552

‎std/os/file.zig‎

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2697,3 +2697,17 @@ pub fn posixWait(pid: i32) i32 {
26972697
}
26982698
}
26992699
}
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‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export fn add(a: i32, b: i32) i32 {
2+
return a + b;
3+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

0 commit comments

Comments
 (0)
Please sign in to comment.