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 librariesmaster
parent
b3a3e2094e
commit
48de57d824
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
15
std/elf.zig
15
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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export fn add(a: i32, b: i32) i32 {
|
||||
return a + b;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue