ranlib.c (7725B)
1 #include <kit/archive.h> 2 #include <kit/core.h> 3 #include <kit/object.h> 4 #include <stdint.h> 5 6 #include "driver.h" 7 #include "inputs.h" 8 9 /* `kit ranlib` — refresh / add a System-V `/` symbol-index member at the 10 * head of an existing POSIX `ar` archive. Equivalent to `kit ar s ARCHIVE`, 11 * which is reserved by the POSIX ar grammar but not yet implemented in the 12 * ar tool. ranlib is the conventional name and the one Makefiles invoke. 13 * 14 * Operation: read all members, rebuild the archive at the same path with 15 * symbol_index=1 and per-member globally-defined symbols filled in. Long 16 * member names are preserved via the `//` extended-name table. Reproducible 17 * output via SOURCE_DATE_EPOCH (same epoch handling as `kit ar`). 18 * 19 * Per-member symbol collection lives in driver/inputs.c 20 * (driver_collect_obj_global_syms) — shared with ar / strip. */ 21 22 #define RANLIB_TOOL "ranlib" 23 24 void driver_help_ranlib(void) { 25 driver_printf( 26 "%.*s", 27 KIT_SLICE_ARG(KIT_SLICE_LIT( 28 "kit ranlib — refresh the symbol index of an `ar` archive\n" 29 "\n" 30 "USAGE\n" 31 " kit ranlib ARCHIVE.a\n" 32 "\n" 33 "DESCRIPTION\n" 34 " Reads every member of ARCHIVE.a, rebuilds the archive in place\n" 35 " with a System-V `/` symbol-index member at the head. Member " 36 "names,\n" 37 " contents, and order are preserved. Reproducible: when\n" 38 " SOURCE_DATE_EPOCH is set to a positive integer, that value is\n" 39 " written to ar_date for every member.\n" 40 "\n" 41 " Equivalent to `kit ar s ARCHIVE.a` (the bare `s` modifier is\n" 42 " reserved by the POSIX ar grammar but not yet implemented).\n" 43 "\n" 44 "OPTIONS\n" 45 " -h, --help Show this help and exit\n" 46 "\n" 47 "EXIT CODES\n" 48 " 0 success 1 archive I/O error 2 bad " 49 "usage\n"))); 50 } 51 52 static uint64_t ranlib_epoch_from_env(void) { 53 const char* s = driver_getenv("SOURCE_DATE_EPOCH"); 54 uint64_t v = 0; 55 if (!s || !*s) return 0; 56 for (; *s; ++s) { 57 if (*s < '0' || *s > '9') return 0; 58 v = v * 10 + (uint64_t)(*s - '0'); 59 } 60 return v; 61 } 62 63 int driver_ranlib(int argc, char** argv) { 64 DriverEnv env; 65 KitContext ctx; 66 KitFileData old_fd = {0}; 67 KitSlice input; 68 KitArIter* it = NULL; 69 KitArMember m; 70 KitArInput* members = NULL; 71 char* name_storage = NULL; 72 size_t name_bytes_total = 0; 73 uint32_t nmembers = 0; 74 uint32_t i; 75 KitArMemberSymbols* msyms = NULL; 76 void** sym_allocs = NULL; 77 size_t* sym_alloc_szs = NULL; 78 KitWriter* out = NULL; 79 KitArWriteOptions opts = {0}; 80 const char* archive_path; 81 int have_old = 0; 82 int rc = 1; 83 84 if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) { 85 driver_help_ranlib(); 86 return 0; 87 } 88 if (argc != 2) { 89 driver_errf(RANLIB_TOOL, "usage: kit ranlib ARCHIVE.a"); 90 return 2; 91 } 92 archive_path = argv[1]; 93 94 driver_env_init(&env); 95 ctx = driver_env_to_context(&env); 96 97 if (ctx.file_io->read_all(ctx.file_io->user, archive_path, &old_fd) != 98 KIT_OK) { 99 driver_errf(RANLIB_TOOL, "failed to read: %.*s", 100 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 101 goto out; 102 } 103 have_old = 1; 104 input.data = old_fd.data; 105 input.len = old_fd.size; 106 107 /* Pass 1: count members and total name bytes (member names returned by 108 * the iterator alias an internal buffer overwritten on each next(), so 109 * we stash a stable copy). */ 110 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 111 driver_errf(RANLIB_TOOL, "not an archive: %.*s", 112 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 113 goto out; 114 } 115 for (;;) { 116 KitIterResult r = kit_ar_iter_next(it, &m); 117 if (r != KIT_ITER_ITEM) break; 118 nmembers++; 119 name_bytes_total += m.name.len + 1; 120 } 121 kit_ar_iter_free(it); 122 it = NULL; 123 124 if (nmembers == 0) { 125 /* Empty archive — still rewrite with an empty symbol index (matches 126 * GNU ranlib's behaviour). */ 127 if (ctx.file_io->open_writer(ctx.file_io->user, archive_path, &out) != 128 KIT_OK) { 129 driver_errf(RANLIB_TOOL, "failed to open: %.*s", 130 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 131 goto out; 132 } 133 opts.epoch = ranlib_epoch_from_env(); 134 opts.long_names = 1; 135 opts.symbol_index = 1; 136 rc = kit_ar_write(out, NULL, 0, &opts) == KIT_OK ? 0 : 1; 137 goto out; 138 } 139 140 members = (KitArInput*)driver_alloc_zeroed( 141 &env, (size_t)nmembers * sizeof(*members)); 142 if (!members) { 143 driver_errf(RANLIB_TOOL, "out of memory"); 144 goto out; 145 } 146 if (name_bytes_total > 0) { 147 name_storage = (char*)driver_alloc_zeroed(&env, name_bytes_total); 148 if (!name_storage) { 149 driver_errf(RANLIB_TOOL, "out of memory"); 150 goto out; 151 } 152 } 153 154 /* Pass 2: copy names and member byte-spans into our parallel arrays. */ 155 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 156 driver_errf(RANLIB_TOOL, "iter re-open failed"); 157 goto out; 158 } 159 { 160 size_t cursor = 0; 161 uint32_t k = 0; 162 while (k < nmembers) { 163 KitIterResult r = kit_ar_iter_next(it, &m); 164 char* dst; 165 size_t j; 166 if (r != KIT_ITER_ITEM) break; 167 dst = name_storage + cursor; 168 for (j = 0; j < m.name.len; ++j) *dst++ = m.name.s[j]; 169 *dst++ = '\0'; 170 members[k].name.s = name_storage + cursor; 171 members[k].name.len = m.name.len; 172 members[k].bytes.data = m.data; 173 members[k].bytes.len = m.size; 174 cursor = (size_t)(dst - name_storage); 175 k++; 176 } 177 } 178 kit_ar_iter_free(it); 179 it = NULL; 180 181 /* Pass 3: collect per-member global symbols. */ 182 msyms = (KitArMemberSymbols*)driver_alloc_zeroed( 183 &env, (size_t)nmembers * sizeof(*msyms)); 184 sym_allocs = 185 (void**)driver_alloc_zeroed(&env, (size_t)nmembers * sizeof(*sym_allocs)); 186 sym_alloc_szs = (size_t*)driver_alloc_zeroed( 187 &env, (size_t)nmembers * sizeof(*sym_alloc_szs)); 188 if (!msyms || !sym_allocs || !sym_alloc_szs) { 189 driver_errf(RANLIB_TOOL, "out of memory"); 190 goto out; 191 } 192 for (i = 0; i < nmembers; ++i) { 193 void* blob = NULL; 194 size_t blob_size = 0; 195 const KitSlice* names = NULL; 196 uint32_t count = 0; 197 if (driver_collect_obj_global_syms(&env, &ctx, RANLIB_TOOL, 198 &members[i].bytes, &blob, &blob_size, 199 &names, &count) != 0) { 200 goto out; 201 } 202 sym_allocs[i] = blob; 203 sym_alloc_szs[i] = blob_size; 204 msyms[i].names = names; 205 msyms[i].count = count; 206 } 207 208 if (ctx.file_io->open_writer(ctx.file_io->user, archive_path, &out) != 209 KIT_OK) { 210 driver_errf(RANLIB_TOOL, "failed to open: %.*s", 211 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 212 goto out; 213 } 214 opts.epoch = ranlib_epoch_from_env(); 215 opts.long_names = 1; 216 opts.symbol_index = 1; 217 opts.member_symbols = msyms; 218 rc = kit_ar_write(out, members, nmembers, &opts) == KIT_OK ? 0 : 1; 219 if (rc == 0 && kit_writer_status(out) != KIT_OK) rc = 1; 220 221 out: 222 if (out) kit_writer_close(out); 223 if (it) kit_ar_iter_free(it); 224 if (sym_allocs) { 225 for (i = 0; i < nmembers; ++i) { 226 if (sym_allocs[i]) driver_free(&env, sym_allocs[i], sym_alloc_szs[i]); 227 } 228 driver_free(&env, sym_allocs, (size_t)nmembers * sizeof(*sym_allocs)); 229 } 230 if (sym_alloc_szs) 231 driver_free(&env, sym_alloc_szs, (size_t)nmembers * sizeof(*sym_alloc_szs)); 232 if (msyms) driver_free(&env, msyms, (size_t)nmembers * sizeof(*msyms)); 233 if (name_storage) driver_free(&env, name_storage, name_bytes_total); 234 if (members) driver_free(&env, members, (size_t)nmembers * sizeof(*members)); 235 if (have_old) ctx.file_io->release(ctx.file_io->user, &old_fd); 236 driver_env_fini(&env); 237 return rc; 238 }