| ... | ... | @@ -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 | | } |