authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-10 22:24:27-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-10 22:24:27-07:00
log8cf40f3445b6548836be3db2a8ef5317af6545ac
tree2a119ef2ba7072bce56ac2fd60b48722a50239c4
parent98583be6e110406a37d0904c50c04d68efbffcd0

stage2: loading glibc metadata


4 files changed, 258 insertions(+), 31 deletions(-)

src-self-hosted/Module.zig+13
...@@ -1256,6 +1256,14 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {...@@ -1256,6 +1256,14 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
1256 mod.c_object_table.putAssumeCapacityNoClobber(c_object, {});1256 mod.c_object_table.putAssumeCapacityNoClobber(c_object, {});
1257 }1257 }
12581258
1259 // If we need to build glibc for the target, add work items for it.
1260 if (mod.bin_file.options.link_libc and
1261 mod.bin_file.options.libc_installation == null and
1262 mod.bin_file.options.target.isGnuLibC())
1263 {
1264 try mod.addBuildingGLibCWorkItems();
1265 }
1266
1259 return mod;1267 return mod;
1260}1268}
12611269
...@@ -4495,3 +4503,8 @@ pub fn get_libc_crt_file(mod: *Module, arena: *Allocator, basename: []const u8)...@@ -4495,3 +4503,8 @@ pub fn get_libc_crt_file(mod: *Module, arena: *Allocator, basename: []const u8)
4495 const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });4503 const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
4496 return full_path;4504 return full_path;
4497}4505}
4506
4507fn addBuildingGLibCWorkItems(mod: *Module) !void {
4508 // crti.o, crtn.o, start.os, abi-note.o, Scrt1.o, libc_nonshared.a
4509 try mod.work_queue.ensureUnusedCapacity(6);
4510}
src-self-hosted/glibc.zig created+231
...@@ -0,0 +1,231 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const target_util = @import("target.zig");
4const mem = std.mem;
5
6pub const Lib = struct {
7 name: []const u8,
8 sover: u8,
9};
10
11pub const Fn = struct {
12 name: []const u8,
13 lib: *const Lib,
14};
15
16pub const VerList = struct {
17 /// 7 is just the max number, we know statically it's big enough.
18 versions: [7]u8,
19 len: u8,
20};
21
22pub const ABI = struct {
23 all_versions: []const std.builtin.Version,
24 all_functions: []const Fn,
25 /// The value is a pointer to all_functions.len items and each item is an index into all_functions.
26 version_table: std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList),
27 arena_state: std.heap.ArenaAllocator.State,
28
29 pub fn destroy(abi: *ABI, gpa: *Allocator) void {
30 abi.version_table.deinit(gpa);
31 abi.arena_state.promote(gpa).deinit(); // Frees the ABI memory too.
32 }
33};
34
35pub const libs = [_]Lib{
36 .{ .name = "c", .sover = 6 },
37 .{ .name = "m", .sover = 6 },
38 .{ .name = "pthread", .sover = 0 },
39 .{ .name = "dl", .sover = 2 },
40 .{ .name = "rt", .sover = 1 },
41 .{ .name = "ld", .sover = 2 },
42 .{ .name = "util", .sover = 1 },
43};
44
45pub const LoadMetaDataError = error{
46 /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
47 ZigInstallationCorrupt,
48 OutOfMemory,
49};
50
51/// This function will emit a log error when there is a problem with the zig installation and then return
52/// `error.ZigInstallationCorrupt`.
53pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError!*ABI {
54 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
55 errdefer arena_allocator.deinit();
56 const arena = &arena_allocator.allocator;
57
58 var all_versions = std.ArrayListUnmanaged(std.builtin.Version){};
59 var all_functions = std.ArrayListUnmanaged(Fn){};
60 var version_table = std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList){};
61 errdefer version_table.deinit(gpa);
62
63 var glibc_dir = zig_lib_dir.openDir("libc" ++ std.fs.path.sep_str ++ "glibc", .{}) catch |err| {
64 std.log.err("unable to open glibc dir: {}", .{@errorName(err)});
65 return error.ZigInstallationCorrupt;
66 };
67 defer glibc_dir.close();
68
69 const max_txt_size = 500 * 1024; // Bigger than this and something is definitely borked.
70 const vers_txt_contents = glibc_dir.readFileAlloc(gpa, "vers.txt", max_txt_size) catch |err| switch (err) {
71 error.OutOfMemory => return error.OutOfMemory,
72 else => {
73 std.log.err("unable to read vers.txt: {}", .{@errorName(err)});
74 return error.ZigInstallationCorrupt;
75 },
76 };
77 defer gpa.free(vers_txt_contents);
78
79 const fns_txt_contents = glibc_dir.readFileAlloc(gpa, "fns.txt", max_txt_size) catch |err| switch (err) {
80 error.OutOfMemory => return error.OutOfMemory,
81 else => {
82 std.log.err("unable to read fns.txt: {}", .{@errorName(err)});
83 return error.ZigInstallationCorrupt;
84 },
85 };
86 defer gpa.free(fns_txt_contents);
87
88 const abi_txt_contents = glibc_dir.readFileAlloc(gpa, "abi.txt", max_txt_size) catch |err| switch (err) {
89 error.OutOfMemory => return error.OutOfMemory,
90 else => {
91 std.log.err("unable to read abi.txt: {}", .{@errorName(err)});
92 return error.ZigInstallationCorrupt;
93 },
94 };
95 defer gpa.free(abi_txt_contents);
96
97 {
98 var it = mem.tokenize(vers_txt_contents, "\r\n");
99 var line_i: usize = 1;
100 while (it.next()) |line| : (line_i += 1) {
101 const prefix = "GLIBC_";
102 if (!mem.startsWith(u8, line, prefix)) {
103 std.log.err("vers.txt:{}: expected 'GLIBC_' prefix", .{line_i});
104 return error.ZigInstallationCorrupt;
105 }
106 const adjusted_line = line[prefix.len..];
107 const ver = std.builtin.Version.parse(adjusted_line) catch |err| {
108 std.log.err("vers.txt:{}: unable to parse glibc version '{}': {}", .{ line_i, line, @errorName(err) });
109 return error.ZigInstallationCorrupt;
110 };
111 try all_versions.append(arena, ver);
112 }
113 }
114 {
115 var file_it = mem.tokenize(fns_txt_contents, "\r\n");
116 var line_i: usize = 1;
117 while (file_it.next()) |line| : (line_i += 1) {
118 var line_it = mem.tokenize(line, " ");
119 const fn_name = line_it.next() orelse {
120 std.log.err("fns.txt:{}: expected function name", .{line_i});
121 return error.ZigInstallationCorrupt;
122 };
123 const lib_name = line_it.next() orelse {
124 std.log.err("fns.txt:{}: expected library name", .{line_i});
125 return error.ZigInstallationCorrupt;
126 };
127 const lib = findLib(lib_name) orelse {
128 std.log.err("fns.txt:{}: unknown library name: {}", .{ line_i, lib_name });
129 return error.ZigInstallationCorrupt;
130 };
131 try all_functions.append(arena, .{
132 .name = fn_name,
133 .lib = lib,
134 });
135 }
136 }
137 {
138 var file_it = mem.split(abi_txt_contents, "\n");
139 var line_i: usize = 0;
140 while (true) {
141 const ver_list_base: []VerList = blk: {
142 const line = file_it.next() orelse break;
143 if (line.len == 0) break;
144 line_i += 1;
145 const ver_list_base = try arena.alloc(VerList, all_functions.items.len);
146 var line_it = mem.tokenize(line, " ");
147 while (line_it.next()) |target_string| {
148 var component_it = mem.tokenize(target_string, "-");
149 const arch_name = component_it.next() orelse {
150 std.log.err("abi.txt:{}: expected arch name", .{line_i});
151 return error.ZigInstallationCorrupt;
152 };
153 const os_name = component_it.next() orelse {
154 std.log.err("abi.txt:{}: expected OS name", .{line_i});
155 return error.ZigInstallationCorrupt;
156 };
157 const abi_name = component_it.next() orelse {
158 std.log.err("abi.txt:{}: expected ABI name", .{line_i});
159 return error.ZigInstallationCorrupt;
160 };
161 const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {
162 std.log.err("abi.txt:{}: unrecognized arch: '{}'", .{ line_i, arch_name });
163 return error.ZigInstallationCorrupt;
164 };
165 if (!mem.eql(u8, os_name, "linux")) {
166 std.log.err("abi.txt:{}: expected OS 'linux', found '{}'", .{ line_i, os_name });
167 return error.ZigInstallationCorrupt;
168 }
169 const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
170 std.log.err("abi.txt:{}: unrecognized ABI: '{}'", .{ line_i, abi_name });
171 return error.ZigInstallationCorrupt;
172 };
173
174 const triple = target_util.ArchOsAbi{
175 .arch = arch_tag,
176 .os = .linux,
177 .abi = abi_tag,
178 };
179 try version_table.put(arena, triple, ver_list_base.ptr);
180 }
181 break :blk ver_list_base;
182 };
183 for (ver_list_base) |*ver_list| {
184 const line = file_it.next() orelse {
185 std.log.err("abi.txt:{}: missing version number line", .{line_i});
186 return error.ZigInstallationCorrupt;
187 };
188 line_i += 1;
189
190 ver_list.* = .{
191 .versions = undefined,
192 .len = 0,
193 };
194 var line_it = mem.tokenize(line, " ");
195 while (line_it.next()) |version_index_string| {
196 if (ver_list.len >= ver_list.versions.len) {
197 // If this happens with legit data, increase the array len in the type.
198 std.log.err("abi.txt:{}: too many versions", .{line_i});
199 return error.ZigInstallationCorrupt;
200 }
201 const version_index = std.fmt.parseInt(u8, version_index_string, 10) catch |err| {
202 // If this happens with legit data, increase the size of the integer type in the struct.
203 std.log.err("abi.txt:{}: unable to parse version: {}", .{ line_i, @errorName(err) });
204 return error.ZigInstallationCorrupt;
205 };
206
207 ver_list.versions[ver_list.len] = version_index;
208 ver_list.len += 1;
209 }
210 }
211 }
212 }
213
214 const abi = try arena.create(ABI);
215 abi.* = .{
216 .all_versions = all_versions.items,
217 .all_functions = all_functions.items,
218 .version_table = version_table,
219 .arena_state = arena_allocator.state,
220 };
221 return abi;
222}
223
224fn findLib(name: []const u8) ?*const Lib {
225 for (libs) |*lib| {
226 if (mem.eql(u8, lib.name, name)) {
227 return lib;
228 }
229 }
230 return null;
231}
src-self-hosted/main.zig+1-1
...@@ -16,7 +16,7 @@ const warn = std.log.warn;...@@ -16,7 +16,7 @@ const warn = std.log.warn;
16const introspect = @import("introspect.zig");16const introspect = @import("introspect.zig");
17const LibCInstallation = @import("libc_installation.zig").LibCInstallation;17const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1818
19fn fatal(comptime format: []const u8, args: anytype) noreturn {19pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
20 std.log.emerg(format, args);20 std.log.emerg(format, args);
21 process.exit(1);21 process.exit(1);
22}22}
src-self-hosted/print_targets.zig+13-30
...@@ -6,8 +6,9 @@ const Allocator = mem.Allocator;...@@ -6,8 +6,9 @@ const Allocator = mem.Allocator;
6const Target = std.Target;6const Target = std.Target;
7const target = @import("target.zig");7const target = @import("target.zig");
8const assert = std.debug.assert;8const assert = std.debug.assert;
99const glibc = @import("glibc.zig");
10const introspect = @import("introspect.zig");10const introspect = @import("introspect.zig");
11const fatal = @import("main.zig").fatal;
1112
12pub fn cmdTargets(13pub fn cmdTargets(
13 allocator: *Allocator,14 allocator: *Allocator,
...@@ -16,33 +17,16 @@ pub fn cmdTargets(...@@ -16,33 +17,16 @@ pub fn cmdTargets(
16 stdout: anytype,17 stdout: anytype,
17 native_target: Target,18 native_target: Target,
18) !void {19) !void {
19 const available_glibcs = blk: {20 const zig_lib_dir_path = introspect.resolveZigLibDir(allocator) catch |err| {
20 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch |err| {21 fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});
21 std.debug.print("unable to find zig installation directory: {}\n", .{@errorName(err)});
22 std.process.exit(1);
23 };
24 defer allocator.free(zig_lib_dir);
25
26 var dir = try std.fs.cwd().openDir(zig_lib_dir, .{});
27 defer dir.close();
28
29 const vers_txt = try dir.readFileAlloc(allocator, "libc" ++ std.fs.path.sep_str ++ "glibc" ++ std.fs.path.sep_str ++ "vers.txt", 10 * 1024);
30 defer allocator.free(vers_txt);
31
32 var list = std.ArrayList(std.builtin.Version).init(allocator);
33 defer list.deinit();
34
35 var it = mem.tokenize(vers_txt, "\r\n");
36 while (it.next()) |line| {
37 const prefix = "GLIBC_";
38 assert(mem.startsWith(u8, line, prefix));
39 const adjusted_line = line[prefix.len..];
40 const ver = try std.builtin.Version.parse(adjusted_line);
41 try list.append(ver);
42 }
43 break :blk list.toOwnedSlice();
44 };22 };
45 defer allocator.free(available_glibcs);23 defer allocator.free(zig_lib_dir_path);
24
25 var zig_lib_dir = try fs.cwd().openDir(zig_lib_dir_path, .{});
26 defer zig_lib_dir.close();
27
28 const glibc_abi = try glibc.loadMetaData(allocator, zig_lib_dir);
29 errdefer glibc_abi.destroy(allocator);
4630
47 var bos = io.bufferedOutStream(stdout);31 var bos = io.bufferedOutStream(stdout);
48 const bos_stream = bos.outStream();32 const bos_stream = bos.outStream();
...@@ -90,10 +74,10 @@ pub fn cmdTargets(...@@ -90,10 +74,10 @@ pub fn cmdTargets(
9074
91 try jws.objectField("glibc");75 try jws.objectField("glibc");
92 try jws.beginArray();76 try jws.beginArray();
93 for (available_glibcs) |glibc| {77 for (glibc_abi.all_versions) |ver| {
94 try jws.arrayElem();78 try jws.arrayElem();
9579
96 const tmp = try std.fmt.allocPrint(allocator, "{}", .{glibc});80 const tmp = try std.fmt.allocPrint(allocator, "{}", .{ver});
97 defer allocator.free(tmp);81 defer allocator.free(tmp);
98 try jws.emitString(tmp);82 try jws.emitString(tmp);
99 }83 }
...@@ -170,7 +154,6 @@ pub fn cmdTargets(...@@ -170,7 +154,6 @@ pub fn cmdTargets(
170 try jws.emitString(@tagName(native_target.os.tag));154 try jws.emitString(@tagName(native_target.os.tag));
171 try jws.objectField("abi");155 try jws.objectField("abi");
172 try jws.emitString(@tagName(native_target.abi));156 try jws.emitString(@tagName(native_target.abi));
173 // TODO implement native glibc version detection in self-hosted
174 try jws.endObject();157 try jws.endObject();
175158
176 try jws.endObject();159 try jws.endObject();