feature. See also
. The project being documented here (as the example) is the Zig library itself.
system.abiAndDynamicLinkerFromFile
fn abiAndDynamicLinkerFromFile(
file_reader: *Io.File.Reader,
header: *const elf.Header,
cpu: Target.Cpu,
os: Target.Os,
ld_info_list: []const LdInfo,
query: Target.Query,
) AbiAndDynamicLinkerFromFileError!Target
File
Code
fn abiAndDynamicLinkerFromFile(
file_reader: *Io.File.Reader,
header: *const elf.Header,
cpu: Target.Cpu,
os: Target.Os,
ld_info_list: []const LdInfo,
query: Target.Query,
) AbiAndDynamicLinkerFromFileError!Target {
const io = file_reader.io;
var result: Target = .{
.cpu = cpu,
.os = os,
.abi = query.abi orelse Target.Abi.default(cpu.arch, os.tag),
.ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch),
.dynamic_linker = query.dynamic_linker orelse .none,
};
var rpath_offset: ?u64 = null;
const look_for_ld = query.dynamic_linker == null;
var got_dyn_section: bool = false;
{
var it = header.iterateProgramHeaders(file_reader);
while (try it.next()) |phdr| switch (phdr.p_type) {
elf.PT_INTERP => {
got_dyn_section = true;
if (look_for_ld) {
const p_filesz = phdr.p_filesz;
if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
const filesz: usize = @intCast(p_filesz);
try file_reader.seekTo(phdr.p_offset);
try file_reader.interface.readSliceAll(result.dynamic_linker.buffer[0..filesz]);
const len = filesz - 1;
// We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
result.dynamic_linker.len = @intCast(len);
const full_ld_path = result.dynamic_linker.buffer[0..len];
for (ld_info_list) |ld_info| {
const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {
result.abi = ld_info.abi;
break;
}
}
}
},
elf.PT_DYNAMIC => {
got_dyn_section = true;
if (builtin.target.os.tag == .linux and result.isGnuLibC() and query.glibc_version == null) {
var dyn_it = header.iterateDynamicSection(file_reader, phdr.p_offset, phdr.p_filesz);
while (try dyn_it.next()) |dyn| {
if (dyn.d_tag == elf.DT_RUNPATH) {
rpath_offset = dyn.d_val;
break;
}
}
}
},
else => continue,
};
}
if (!got_dyn_section) {
return error.StaticElfFile;
}
if (builtin.target.os.tag == .linux and result.isGnuLibC() and query.glibc_version == null) {
const str_section_off = header.shoff + @as(u64, header.shentsize) * @as(u64, header.shstrndx);
try file_reader.seekTo(str_section_off);
const shstr = try elf.takeSectionHeader(&file_reader.interface, header.is_64, header.endian);
var strtab_buf: [4096]u8 = undefined;
const shstrtab = strtab_buf[0..@min(shstr.sh_size, strtab_buf.len)];
try file_reader.seekTo(shstr.sh_offset);
try file_reader.interface.readSliceAll(shstrtab);
const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: {
var it = header.iterateSectionHeaders(file_reader);
while (try it.next()) |shdr| {
const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;
const sh_name = shstrtab[shdr.sh_name..end :0];
if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{
.offset = shdr.sh_offset,
.size = shdr.sh_size,
};
} else break :find_dyn_str null;
};
if (dynstr) |ds| {
if (rpath_offset) |rpoff| {
if (rpoff > ds.size) return error.InvalidElfFile;
const rpoff_file = ds.offset + rpoff;
const rp_max_size = ds.size - rpoff;
try file_reader.seekTo(rpoff_file);
const rpath_list = try file_reader.interface.takeSentinel(0);
if (rpath_list.len > rp_max_size) return error.StreamTooLong;
var it = mem.tokenizeScalar(u8, rpath_list, ':');
while (it.next()) |rpath| {
if (glibcVerFromRPath(io, rpath)) |ver| {
result.os.version_range.linux.glibc = ver;
return result;
} else |err| switch (err) {
error.GLibCNotFound => continue,
else => |e| return e,
}
}
}
}
if (result.dynamic_linker.get()) |dl_path| glibc_ver: {
// directory as the dynamic linker.
if (fs.path.dirname(dl_path)) |rpath| {
if (glibcVerFromRPath(io, rpath)) |ver| {
result.os.version_range.linux.glibc = ver;
return result;
} else |err| switch (err) {
error.GLibCNotFound => {},
else => |e| return e,
}
}
// present in the symlink data for the dynamic linker path.
var link_buffer: [posix.PATH_MAX]u8 = undefined;
const link_name = if (Io.Dir.readLinkAbsolute(io, dl_path, &link_buffer)) |n|
link_buffer[0..n]
else |err| switch (err) {
error.NameTooLong => unreachable,
error.BadPathName => unreachable,
error.UnsupportedReparsePointType => unreachable,
error.NetworkNotFound => unreachable,
error.AntivirusInterference => unreachable,
error.FileBusy => unreachable,
error.AccessDenied,
error.PermissionDenied,
error.FileNotFound,
error.NotLink,
error.NotDir,
=> break :glibc_ver,
error.SystemResources,
error.FileSystem,
error.SymLinkLoop,
error.Canceled,
error.Unexpected,
=> |e| return e,
};
result.os.version_range.linux.glibc = glibcVerFromLinkName(
fs.path.basename(link_name),
"ld-",
) catch |err| switch (err) {
error.UnrecognizedGnuLibCFileName,
error.InvalidGnuLibCVersion,
=> break :glibc_ver,
};
return result;
}
// Some distros such as Debian keep their libc.so.6 in `/lib/$triple/`.
var path_buf: [posix.PATH_MAX]u8 = undefined;
var index: usize = 0;
const prefix = "/lib/";
const cpu_arch = @tagName(result.cpu.arch);
const os_tag = @tagName(result.os.tag);
const abi = @tagName(result.abi);
@memcpy(path_buf[index..][0..prefix.len], prefix);
index += prefix.len;
@memcpy(path_buf[index..][0..cpu_arch.len], cpu_arch);
index += cpu_arch.len;
path_buf[index] = '-';
index += 1;
@memcpy(path_buf[index..][0..os_tag.len], os_tag);
index += os_tag.len;
path_buf[index] = '-';
index += 1;
@memcpy(path_buf[index..][0..abi.len], abi);
index += abi.len;
const rpath = path_buf[0..index];
if (glibcVerFromRPath(io, rpath)) |ver| {
result.os.version_range.linux.glibc = ver;
return result;
} else |err| switch (err) {
error.GLibCNotFound => {},
else => |e| return e,
}
}
return result;
}