| ... | @@ -1,343 +0,0 @@ |
| 1 | const std = @import("std"); |
| 2 | const fs = std.fs; |
| 3 | const fmt = std.fmt; |
| 4 | const assert = std.debug.assert; |
| 5 | |
| 6 | // Example abilist path: |
| 7 | // ./sysdeps/unix/sysv/linux/aarch64/libc.abilist |
| 8 | const AbiList = struct { |
| 9 | targets: []const ZigTarget, |
| 10 | path: []const u8, |
| 11 | }; |
| 12 | const ZigTarget = struct { |
| 13 | arch: std.Target.Cpu.Arch, |
| 14 | abi: std.Target.Abi, |
| 15 | }; |
| 16 | |
| 17 | const lib_names = [_][]const u8{ |
| 18 | "c", |
| 19 | "dl", |
| 20 | "m", |
| 21 | "pthread", |
| 22 | "rt", |
| 23 | "ld", |
| 24 | "util", |
| 25 | }; |
| 26 | |
| 27 | // fpu/nofpu are hardcoded elsewhere, based on .gnueabi/.gnueabihf with an exception for .arm |
| 28 | // n64/n32 are hardcoded elsewhere, based on .gnuabi64/.gnuabin32 |
| 29 | const abi_lists = [_]AbiList{ |
| 30 | AbiList{ |
| 31 | .targets = &[_]ZigTarget{ |
| 32 | ZigTarget{ .arch = .aarch64, .abi = .gnu }, |
| 33 | ZigTarget{ .arch = .aarch64_be, .abi = .gnu }, |
| 34 | }, |
| 35 | .path = "aarch64", |
| 36 | }, |
| 37 | AbiList{ |
| 38 | .targets = &[_]ZigTarget{ZigTarget{ .arch = .s390x, .abi = .gnu }}, |
| 39 | .path = "s390/s390-64", |
| 40 | }, |
| 41 | AbiList{ |
| 42 | .targets = &[_]ZigTarget{ |
| 43 | ZigTarget{ .arch = .arm, .abi = .gnueabi }, |
| 44 | ZigTarget{ .arch = .armeb, .abi = .gnueabi }, |
| 45 | ZigTarget{ .arch = .arm, .abi = .gnueabihf }, |
| 46 | ZigTarget{ .arch = .armeb, .abi = .gnueabihf }, |
| 47 | }, |
| 48 | .path = "arm", |
| 49 | }, |
| 50 | AbiList{ |
| 51 | .targets = &[_]ZigTarget{ |
| 52 | ZigTarget{ .arch = .sparc, .abi = .gnu }, |
| 53 | ZigTarget{ .arch = .sparcel, .abi = .gnu }, |
| 54 | }, |
| 55 | .path = "sparc/sparc32", |
| 56 | }, |
| 57 | AbiList{ |
| 58 | .targets = &[_]ZigTarget{ZigTarget{ .arch = .sparcv9, .abi = .gnu }}, |
| 59 | .path = "sparc/sparc64", |
| 60 | }, |
| 61 | AbiList{ |
| 62 | .targets = &[_]ZigTarget{ |
| 63 | ZigTarget{ .arch = .mips64el, .abi = .gnuabi64 }, |
| 64 | ZigTarget{ .arch = .mips64, .abi = .gnuabi64 }, |
| 65 | }, |
| 66 | .path = "mips/mips64", |
| 67 | }, |
| 68 | AbiList{ |
| 69 | .targets = &[_]ZigTarget{ |
| 70 | ZigTarget{ .arch = .mips64el, .abi = .gnuabin32 }, |
| 71 | ZigTarget{ .arch = .mips64, .abi = .gnuabin32 }, |
| 72 | }, |
| 73 | .path = "mips/mips64", |
| 74 | }, |
| 75 | AbiList{ |
| 76 | .targets = &[_]ZigTarget{ |
| 77 | ZigTarget{ .arch = .mipsel, .abi = .gnueabihf }, |
| 78 | ZigTarget{ .arch = .mips, .abi = .gnueabihf }, |
| 79 | }, |
| 80 | .path = "mips/mips32", |
| 81 | }, |
| 82 | AbiList{ |
| 83 | .targets = &[_]ZigTarget{ |
| 84 | ZigTarget{ .arch = .mipsel, .abi = .gnueabi }, |
| 85 | ZigTarget{ .arch = .mips, .abi = .gnueabi }, |
| 86 | }, |
| 87 | .path = "mips/mips32", |
| 88 | }, |
| 89 | AbiList{ |
| 90 | .targets = &[_]ZigTarget{ZigTarget{ .arch = .x86_64, .abi = .gnu }}, |
| 91 | .path = "x86_64/64", |
| 92 | }, |
| 93 | AbiList{ |
| 94 | .targets = &[_]ZigTarget{ZigTarget{ .arch = .x86_64, .abi = .gnux32 }}, |
| 95 | .path = "x86_64/x32", |
| 96 | }, |
| 97 | AbiList{ |
| 98 | .targets = &[_]ZigTarget{ZigTarget{ .arch = .i386, .abi = .gnu }}, |
| 99 | .path = "i386", |
| 100 | }, |
| 101 | AbiList{ |
| 102 | .targets = &[_]ZigTarget{ZigTarget{ .arch = .powerpc64le, .abi = .gnu }}, |
| 103 | .path = "powerpc/powerpc64/le", |
| 104 | }, |
| 105 | AbiList{ |
| 106 | .targets = &[_]ZigTarget{ZigTarget{ .arch = .powerpc64, .abi = .gnu }}, |
| 107 | .path = "powerpc/powerpc64/be", |
| 108 | }, |
| 109 | AbiList{ |
| 110 | .targets = &[_]ZigTarget{ |
| 111 | ZigTarget{ .arch = .powerpc, .abi = .gnueabi }, |
| 112 | ZigTarget{ .arch = .powerpc, .abi = .gnueabihf }, |
| 113 | }, |
| 114 | .path = "powerpc/powerpc32", |
| 115 | }, |
| 116 | }; |
| 117 | |
| 118 | const FunctionSet = struct { |
| 119 | list: std.ArrayList(VersionedFn), |
| 120 | fn_vers_list: FnVersionList, |
| 121 | }; |
| 122 | const FnVersionList = std.StringHashMap(std.ArrayList(usize)); |
| 123 | |
| 124 | const VersionedFn = struct { |
| 125 | ver: []const u8, // example: "GLIBC_2.15" |
| 126 | name: []const u8, // example: "puts" |
| 127 | }; |
| 128 | const Function = struct { |
| 129 | name: []const u8, // example: "puts" |
| 130 | lib: []const u8, // example: "c" |
| 131 | index: usize, |
| 132 | }; |
| 133 | |
| 134 | pub fn main() !void { |
| 135 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 136 | const allocator = arena.allocator(); |
| 137 | const args = try std.process.argsAlloc(allocator); |
| 138 | const in_glibc_dir = args[1]; // path to the unzipped tarball of glibc, e.g. ~/downloads/glibc-2.25 |
| 139 | const zig_src_dir = args[2]; // path to the source checkout of zig, lib dir, e.g. ~/zig-src/lib |
| 140 | |
| 141 | const prefix = try fs.path.join(allocator, &[_][]const u8{ in_glibc_dir, "sysdeps", "unix", "sysv", "linux" }); |
| 142 | const glibc_out_dir = try fs.path.join(allocator, &[_][]const u8{ zig_src_dir, "libc", "glibc" }); |
| 143 | |
| 144 | var global_fn_set = std.StringHashMap(Function).init(allocator); |
| 145 | var global_ver_set = std.StringHashMap(usize).init(allocator); |
| 146 | var target_functions = std.AutoHashMap(usize, FunctionSet).init(allocator); |
| 147 | |
| 148 | for (abi_lists) |*abi_list| { |
| 149 | const target_funcs_gop = try target_functions.getOrPut(@ptrToInt(abi_list)); |
| 150 | if (!target_funcs_gop.found_existing) { |
| 151 | target_funcs_gop.value_ptr.* = FunctionSet{ |
| 152 | .list = std.ArrayList(VersionedFn).init(allocator), |
| 153 | .fn_vers_list = FnVersionList.init(allocator), |
| 154 | }; |
| 155 | } |
| 156 | const fn_set = &target_funcs_gop.value_ptr.list; |
| 157 | |
| 158 | for (lib_names) |lib_name| { |
| 159 | const lib_prefix = if (std.mem.eql(u8, lib_name, "ld")) "" else "lib"; |
| 160 | const basename = try fmt.allocPrint(allocator, "{s}{s}.abilist", .{ lib_prefix, lib_name }); |
| 161 | const abi_list_filename = blk: { |
| 162 | const is_c = std.mem.eql(u8, lib_name, "c"); |
| 163 | const is_m = std.mem.eql(u8, lib_name, "m"); |
| 164 | const is_ld = std.mem.eql(u8, lib_name, "ld"); |
| 165 | if (abi_list.targets[0].abi == .gnuabi64 and (is_c or is_ld)) { |
| 166 | break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "n64", basename }); |
| 167 | } else if (abi_list.targets[0].abi == .gnuabin32 and (is_c or is_ld)) { |
| 168 | break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "n32", basename }); |
| 169 | } else if (abi_list.targets[0].arch != .arm and |
| 170 | abi_list.targets[0].abi == .gnueabihf and |
| 171 | (is_c or (is_m and abi_list.targets[0].arch == .powerpc))) |
| 172 | { |
| 173 | break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "fpu", basename }); |
| 174 | } else if (abi_list.targets[0].arch != .arm and |
| 175 | abi_list.targets[0].abi == .gnueabi and |
| 176 | (is_c or (is_m and abi_list.targets[0].arch == .powerpc))) |
| 177 | { |
| 178 | break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "nofpu", basename }); |
| 179 | } else if (abi_list.targets[0].arch == .arm) { |
| 180 | break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "le", basename }); |
| 181 | } else if (abi_list.targets[0].arch == .armeb) { |
| 182 | break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "be", basename }); |
| 183 | } |
| 184 | break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, basename }); |
| 185 | }; |
| 186 | const max_bytes = 10 * 1024 * 1024; |
| 187 | const contents = std.fs.cwd().readFileAlloc(allocator, abi_list_filename, max_bytes) catch |err| { |
| 188 | std.debug.print("unable to open {s}: {}\n", .{ abi_list_filename, err }); |
| 189 | std.process.exit(1); |
| 190 | }; |
| 191 | var lines_it = std.mem.tokenize(u8, contents, "\n"); |
| 192 | while (lines_it.next()) |line| { |
| 193 | var tok_it = std.mem.tokenize(u8, line, " "); |
| 194 | const ver = tok_it.next().?; |
| 195 | const name = tok_it.next().?; |
| 196 | const category = tok_it.next().?; |
| 197 | if (!std.mem.eql(u8, category, "F") and |
| 198 | !std.mem.eql(u8, category, "D")) |
| 199 | { |
| 200 | continue; |
| 201 | } |
| 202 | if (std.mem.startsWith(u8, ver, "GCC_")) continue; |
| 203 | try global_ver_set.put(ver, undefined); |
| 204 | const gop = try global_fn_set.getOrPut(name); |
| 205 | if (gop.found_existing) { |
| 206 | if (!std.mem.eql(u8, gop.value_ptr.lib, "c")) { |
| 207 | gop.value_ptr.lib = lib_name; |
| 208 | } |
| 209 | } else { |
| 210 | gop.value_ptr.* = Function{ |
| 211 | .name = name, |
| 212 | .lib = lib_name, |
| 213 | .index = undefined, |
| 214 | }; |
| 215 | } |
| 216 | try fn_set.append(VersionedFn{ |
| 217 | .ver = ver, |
| 218 | .name = name, |
| 219 | }); |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | const global_fn_list = blk: { |
| 225 | var list = std.ArrayList([]const u8).init(allocator); |
| 226 | var it = global_fn_set.keyIterator(); |
| 227 | while (it.next()) |key| try list.append(key.*); |
| 228 | std.sort.sort([]const u8, list.items, {}, strCmpLessThan); |
| 229 | break :blk list.items; |
| 230 | }; |
| 231 | const global_ver_list = blk: { |
| 232 | var list = std.ArrayList([]const u8).init(allocator); |
| 233 | var it = global_ver_set.keyIterator(); |
| 234 | while (it.next()) |key| try list.append(key.*); |
| 235 | std.sort.sort([]const u8, list.items, {}, versionLessThan); |
| 236 | break :blk list.items; |
| 237 | }; |
| 238 | { |
| 239 | const vers_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "vers.txt" }); |
| 240 | const vers_txt_file = try fs.cwd().createFile(vers_txt_path, .{}); |
| 241 | defer vers_txt_file.close(); |
| 242 | var buffered = std.io.bufferedWriter(vers_txt_file.writer()); |
| 243 | const vers_txt = buffered.writer(); |
| 244 | for (global_ver_list) |name, i| { |
| 245 | global_ver_set.put(name, i) catch unreachable; |
| 246 | try vers_txt.print("{s}\n", .{name}); |
| 247 | } |
| 248 | try buffered.flush(); |
| 249 | } |
| 250 | { |
| 251 | const fns_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "fns.txt" }); |
| 252 | const fns_txt_file = try fs.cwd().createFile(fns_txt_path, .{}); |
| 253 | defer fns_txt_file.close(); |
| 254 | var buffered = std.io.bufferedWriter(fns_txt_file.writer()); |
| 255 | const fns_txt = buffered.writer(); |
| 256 | for (global_fn_list) |name, i| { |
| 257 | const value = global_fn_set.getPtr(name).?; |
| 258 | value.index = i; |
| 259 | try fns_txt.print("{s} {s}\n", .{ name, value.lib }); |
| 260 | } |
| 261 | try buffered.flush(); |
| 262 | } |
| 263 | |
| 264 | // Now the mapping of version and function to integer index is complete. |
| 265 | // Here we create a mapping of function name to list of versions. |
| 266 | for (abi_lists) |*abi_list| { |
| 267 | const value = target_functions.getPtr(@ptrToInt(abi_list)).?; |
| 268 | const fn_vers_list = &value.fn_vers_list; |
| 269 | for (value.list.items) |*ver_fn| { |
| 270 | const gop = try fn_vers_list.getOrPut(ver_fn.name); |
| 271 | if (!gop.found_existing) { |
| 272 | gop.value_ptr.* = std.ArrayList(usize).init(allocator); |
| 273 | } |
| 274 | const ver_index = global_ver_set.get(ver_fn.ver).?; |
| 275 | if (std.mem.indexOfScalar(usize, gop.value_ptr.items, ver_index) == null) { |
| 276 | try gop.value_ptr.append(ver_index); |
| 277 | } |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | { |
| 282 | const abilist_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "abi.txt" }); |
| 283 | const abilist_txt_file = try fs.cwd().createFile(abilist_txt_path, .{}); |
| 284 | defer abilist_txt_file.close(); |
| 285 | var buffered = std.io.bufferedWriter(abilist_txt_file.writer()); |
| 286 | const abilist_txt = buffered.writer(); |
| 287 | |
| 288 | // first iterate over the abi lists |
| 289 | for (abi_lists) |*abi_list| { |
| 290 | const fn_vers_list = &target_functions.getPtr(@ptrToInt(abi_list)).?.fn_vers_list; |
| 291 | for (abi_list.targets) |target, it_i| { |
| 292 | if (it_i != 0) try abilist_txt.writeByte(' '); |
| 293 | try abilist_txt.print("{s}-linux-{s}", .{ @tagName(target.arch), @tagName(target.abi) }); |
| 294 | } |
| 295 | try abilist_txt.writeByte('\n'); |
| 296 | // next, each line implicitly corresponds to a function |
| 297 | for (global_fn_list) |name| { |
| 298 | const value = fn_vers_list.getPtr(name) orelse { |
| 299 | try abilist_txt.writeByte('\n'); |
| 300 | continue; |
| 301 | }; |
| 302 | for (value.items) |ver_index, it_i| { |
| 303 | if (it_i != 0) try abilist_txt.writeByte(' '); |
| 304 | try abilist_txt.print("{d}", .{ver_index}); |
| 305 | } |
| 306 | try abilist_txt.writeByte('\n'); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | try buffered.flush(); |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | pub fn strCmpLessThan(context: void, a: []const u8, b: []const u8) bool { |
| 315 | _ = context; |
| 316 | return std.mem.order(u8, a, b) == .lt; |
| 317 | } |
| 318 | |
| 319 | pub fn versionLessThan(context: void, a: []const u8, b: []const u8) bool { |
| 320 | _ = context; |
| 321 | const sep_chars = "GLIBC_."; |
| 322 | var a_tokens = std.mem.tokenize(u8, a, sep_chars); |
| 323 | var b_tokens = std.mem.tokenize(u8, b, sep_chars); |
| 324 | |
| 325 | while (true) { |
| 326 | const a_next = a_tokens.next(); |
| 327 | const b_next = b_tokens.next(); |
| 328 | if (a_next == null and b_next == null) { |
| 329 | return false; // equal means not less than |
| 330 | } else if (a_next == null) { |
| 331 | return true; |
| 332 | } else if (b_next == null) { |
| 333 | return false; |
| 334 | } |
| 335 | const a_int = fmt.parseInt(u64, a_next.?, 10) catch unreachable; |
| 336 | const b_int = fmt.parseInt(u64, b_next.?, 10) catch unreachable; |
| 337 | if (a_int < b_int) { |
| 338 | return true; |
| 339 | } else if (a_int > b_int) { |
| 340 | return false; |
| 341 | } |
| 342 | } |
| 343 | } |