size.c (11281B)
1 #include <kit/archive.h> 2 #include <kit/core.h> 3 #include <kit/object.h> 4 #include <stdint.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 9 #include "driver.h" 10 11 #define SIZE_TOOL "size" 12 13 typedef enum SizeFmt { 14 SIZE_FMT_BERKELEY, 15 SIZE_FMT_SYSV, 16 } SizeFmt; 17 18 typedef enum SizeRadix { 19 SIZE_RAD_HEX, 20 SIZE_RAD_DEC, 21 SIZE_RAD_OCT, 22 } SizeRadix; 23 24 typedef struct SizeOpts { 25 SizeFmt fmt; /* -A => SYSV, default BERKELEY */ 26 SizeRadix radix; /* default hex; -d => DEC, -o => OCT */ 27 int common; /* --common */ 28 int totals; /* -t / --totals (berkeley only) */ 29 } SizeOpts; 30 31 /* Section size classification + aggregation now live in libkit 32 * (kit_obj_section_size_class / kit_obj_size_totals). size_compute_obj is a 33 * thin shim that maps the tool's --common flag onto the public totals call. */ 34 static KitObjSizeTotals size_compute_obj(KitObjFile* of, const SizeOpts* opts) { 35 KitObjSizeTotals a; 36 if (kit_obj_size_totals(of, opts->common != 0, &a) != KIT_OK) 37 memset(&a, 0, sizeof a); 38 return a; 39 } 40 41 static void size_print_val(uint64_t v, SizeRadix radix, int width, char* buf, 42 size_t buf_size) { 43 switch (radix) { 44 case SIZE_RAD_DEC: 45 snprintf(buf, buf_size, "%*llu", width, (unsigned long long)v); 46 break; 47 case SIZE_RAD_OCT: 48 snprintf(buf, buf_size, "%*llo", width, (unsigned long long)v); 49 break; 50 default: /* hex */ 51 snprintf(buf, buf_size, "%*llx", width, (unsigned long long)v); 52 break; 53 } 54 } 55 56 static int size_width(SizeRadix radix) { 57 return (radix == SIZE_RAD_OCT) ? 10 : (radix == SIZE_RAD_DEC) ? 8 : 7; 58 } 59 60 static void size_print_berkeley_header(const SizeOpts* opts) { 61 int w = size_width(opts->radix); 62 driver_printf(" %-*s %-*s %-*s %8s %7s filename\n", w, "text", w, 63 "data", w, "bss", "dec", "hex"); 64 } 65 66 static void size_begin_output(const SizeOpts* opts, int* begun) { 67 if (*begun) return; 68 if (opts->fmt == SIZE_FMT_BERKELEY) size_print_berkeley_header(opts); 69 *begun = 1; 70 } 71 72 static void size_print_berkeley_sums(const KitObjSizeTotals* a, const char* name, 73 const SizeOpts* opts) { 74 int w = size_width(opts->radix); 75 char text_buf[32], data_buf[32], bss_buf[32], dec_buf[32], hex_buf[32]; 76 size_print_val(a->text, opts->radix, w, text_buf, sizeof text_buf); 77 size_print_val(a->data, opts->radix, w, data_buf, sizeof data_buf); 78 size_print_val(a->bss, opts->radix, w, bss_buf, sizeof bss_buf); 79 size_print_val(a->total, SIZE_RAD_DEC, 8, dec_buf, sizeof dec_buf); 80 size_print_val(a->total, SIZE_RAD_HEX, 7, hex_buf, sizeof hex_buf); 81 driver_printf(" %s %s %s %s %s %s\n", text_buf, data_buf, bss_buf, 82 dec_buf, hex_buf, name); 83 } 84 85 static void size_print_sysv(KitObjFile* of, const char* name, 86 const SizeOpts* opts) { 87 uint32_t ns, i; 88 driver_printf("%s :\n", name); 89 driver_printf("section size addr\n"); 90 ns = kit_obj_nsections(of); 91 for (i = 0; i < ns; ++i) { 92 KitObjSecInfo sec; 93 if (kit_obj_section(of, i, &sec) != KIT_OK) continue; 94 if (!(sec.flags & KIT_SF_ALLOC)) continue; 95 if (sec.kind == KIT_SEC_DEBUG) continue; 96 /* addr is the load vaddr in a linked image, 0 for relocatables. */ 97 driver_printf("%-16.16s %08llx %08llx\n", 98 sec.name.len ? sec.name.s : "(anon)", 99 (unsigned long long)sec.size, (unsigned long long)sec.addr); 100 } 101 { 102 KitObjSizeTotals a = size_compute_obj(of, opts); 103 driver_printf("Total %08llx\n", (unsigned long long)a.total); 104 } 105 } 106 107 static int size_process_file(const KitContext* ctx, const KitSlice* input, 108 const char* path, const SizeOpts* opts, 109 KitObjSizeTotals* total_out, int* any_out, 110 int* output_begun) { 111 KitBinFmt fmt = kit_detect_fmt(input->data, input->len); 112 if (fmt == KIT_BIN_AR) { 113 KitArIter* it = NULL; 114 int failed = 0; 115 int valid_members = 0; 116 if (kit_ar_iter_new(ctx, input, &it) != KIT_OK) { 117 driver_errf(SIZE_TOOL, "%s: not a recognized archive", path); 118 return 1; 119 } 120 for (;;) { 121 KitArMember m; 122 KitIterResult ir = kit_ar_iter_next(it, &m); 123 if (ir == KIT_ITER_END) break; 124 if (ir == KIT_ITER_ERROR) { 125 driver_errf(SIZE_TOOL, "%s: corrupt archive", path); 126 failed = 1; 127 break; 128 } 129 KitObjFile* of = NULL; 130 KitSlice mb; 131 char nmbuf[512]; 132 mb.data = m.data; 133 mb.len = m.size; 134 snprintf(nmbuf, sizeof nmbuf, "%s(%.*s)", path, (int)m.name.len, 135 m.name.s); 136 if (kit_obj_open(ctx, kit_slice_cstr(nmbuf), &mb, &of) == KIT_OK) { 137 KitObjSizeTotals a = size_compute_obj(of, opts); 138 size_begin_output(opts, output_begun); 139 if (opts->fmt == SIZE_FMT_BERKELEY) { 140 size_print_berkeley_sums(&a, nmbuf, opts); 141 } else { 142 size_print_sysv(of, nmbuf, opts); 143 } 144 total_out->text += a.text; 145 total_out->data += a.data; 146 total_out->bss += a.bss; 147 total_out->total += a.total; 148 *any_out = 1; 149 ++valid_members; 150 kit_obj_free(of); 151 } else { 152 driver_errf(SIZE_TOOL, "%s: not a recognized object file", nmbuf); 153 failed = 1; 154 } 155 } 156 kit_ar_iter_free(it); 157 if (!valid_members && !failed) { 158 driver_errf(SIZE_TOOL, "%s: archive contains no object members", path); 159 failed = 1; 160 } 161 return failed; 162 } else { 163 KitObjFile* of = NULL; 164 if (kit_obj_open(ctx, kit_slice_cstr(path), input, &of) != KIT_OK) { 165 driver_errf(SIZE_TOOL, "%s: not a recognized object file", path); 166 return 1; 167 } 168 { 169 KitObjSizeTotals a = size_compute_obj(of, opts); 170 size_begin_output(opts, output_begun); 171 if (opts->fmt == SIZE_FMT_BERKELEY) { 172 size_print_berkeley_sums(&a, path, opts); 173 } else { 174 size_print_sysv(of, path, opts); 175 } 176 total_out->text += a.text; 177 total_out->data += a.data; 178 total_out->bss += a.bss; 179 total_out->total += a.total; 180 } 181 *any_out = 1; 182 kit_obj_free(of); 183 } 184 return 0; 185 } 186 187 void driver_help_size(void) { 188 driver_printf( 189 "%.*s", 190 KIT_SLICE_ARG(KIT_SLICE_LIT( 191 "kit size — display section sizes of objects and linked images\n" 192 "\n" 193 "USAGE\n" 194 " kit size [OPTIONS] FILE...\n" 195 "\n" 196 "DESCRIPTION\n" 197 " Reads ELF, Mach-O, COFF/PE, and Wasm objects or linked images;\n" 198 " POSIX ar archives are reported member by member. The default is\n" 199 " Berkeley format in decimal: text, data, bss, total decimal/hex,\n" 200 " and filename. SysV format prints each section and its address.\n" 201 "\n" 202 "OPTIONS\n" 203 " -A, --format sysv SysV output (section-by-section)\n" 204 " -B, --format berkeley Berkeley output (default)\n" 205 " -d sizes in decimal\n" 206 " -o sizes in octal\n" 207 " -x sizes in hexadecimal\n" 208 " --common include COMMON symbols in bss total\n" 209 " -t, --totals print a totals line (Berkeley only)\n" 210 " -h, --help show this help\n" 211 "\n" 212 "EXAMPLES\n" 213 " kit size app\n" 214 " kit size -A -x app\n" 215 " kit size -B -t a.o b.o\n" 216 " kit size libfoo.a\n" 217 "\n" 218 "EXIT CODES\n" 219 " 0 success 1 object/archive or I/O error\n" 220 " 2 bad usage\n"))); 221 } 222 223 int driver_size(int argc, char** argv) { 224 DriverEnv env; 225 KitContext ctx; 226 SizeOpts opts; 227 KitObjSizeTotals totals; 228 int i, rc = 1, any_input = 0, any_output = 0; 229 int output_begun = 0, had_error = 0, options_done = 0; 230 231 if (argc < 2) { 232 driver_help_size(); 233 return 0; 234 } 235 236 memset(&opts, 0, sizeof opts); 237 memset(&totals, 0, sizeof totals); 238 opts.radix = SIZE_RAD_HEX; 239 driver_env_init(&env); 240 ctx = driver_env_to_context(&env); 241 242 for (i = 1; i < argc; ++i) { 243 const char* a = argv[i]; 244 if (!options_done && driver_streq(a, "--")) { 245 options_done = 1; 246 continue; 247 } 248 if (!options_done && 249 (driver_streq(a, "-h") || driver_streq(a, "--help"))) { 250 driver_help_size(); 251 rc = 0; 252 goto done; 253 } 254 if (!options_done && 255 (driver_streq(a, "-A") || driver_streq(a, "--format=sysv"))) { 256 opts.fmt = SIZE_FMT_SYSV; 257 continue; 258 } 259 if (!options_done && 260 (driver_streq(a, "-B") || driver_streq(a, "--format=berkeley"))) { 261 opts.fmt = SIZE_FMT_BERKELEY; 262 continue; 263 } 264 if (!options_done && driver_streq(a, "-d")) { 265 opts.radix = SIZE_RAD_DEC; 266 continue; 267 } 268 if (!options_done && driver_streq(a, "-o")) { 269 opts.radix = SIZE_RAD_OCT; 270 continue; 271 } 272 if (!options_done && driver_streq(a, "-x")) { 273 opts.radix = SIZE_RAD_HEX; 274 continue; 275 } 276 if (!options_done && driver_streq(a, "--common")) { 277 opts.common = 1; 278 continue; 279 } 280 if (!options_done && 281 (driver_streq(a, "-t") || driver_streq(a, "--totals"))) { 282 opts.totals = 1; 283 continue; 284 } 285 if (!options_done && a[0] == '-') { 286 driver_errf(SIZE_TOOL, "unknown option: %s", a); 287 rc = 2; 288 goto done; 289 } 290 any_input = 1; 291 } 292 293 if (!any_input) { 294 driver_errf(SIZE_TOOL, "no input files"); 295 rc = 2; 296 goto done; 297 } 298 299 options_done = 0; 300 for (i = 1; i < argc; ++i) { 301 const char* a = argv[i]; 302 if (!options_done && driver_streq(a, "--")) { 303 options_done = 1; 304 continue; 305 } 306 if (!options_done && 307 (driver_streq(a, "-A") || driver_streq(a, "--format=sysv") || 308 driver_streq(a, "-B") || driver_streq(a, "--format=berkeley") || 309 driver_streq(a, "-d") || driver_streq(a, "-o") || 310 driver_streq(a, "-x") || driver_streq(a, "--common") || 311 driver_streq(a, "-t") || driver_streq(a, "--totals"))) { 312 continue; 313 } 314 { 315 const char* path = a; 316 DriverLoad ld = {0}; 317 KitSlice input; 318 if (driver_load_bytes(&env.file_io, SIZE_TOOL, path, &ld, &input) != 0) { 319 had_error = 1; 320 continue; 321 } 322 if (size_process_file(&ctx, &input, path, &opts, &totals, &any_output, 323 &output_begun) != 0) 324 had_error = 1; 325 driver_release_bytes(&env.file_io, &ld); 326 } 327 } 328 329 if (!any_output) { 330 rc = 1; 331 goto done; 332 } 333 334 if (opts.totals && opts.fmt == SIZE_FMT_BERKELEY) { 335 int w = size_width(opts.radix); 336 char text_buf[32], data_buf[32], bss_buf[32], dec_buf[32], hex_buf[32]; 337 size_print_val(totals.text, opts.radix, w, text_buf, sizeof text_buf); 338 size_print_val(totals.data, opts.radix, w, data_buf, sizeof data_buf); 339 size_print_val(totals.bss, opts.radix, w, bss_buf, sizeof bss_buf); 340 size_print_val(totals.total, SIZE_RAD_DEC, 8, dec_buf, sizeof dec_buf); 341 size_print_val(totals.total, SIZE_RAD_HEX, 7, hex_buf, sizeof hex_buf); 342 driver_printf(" %s %s %s %s %s (TOTALS)\n", text_buf, data_buf, bss_buf, 343 dec_buf, hex_buf); 344 } 345 346 rc = had_error ? 1 : 0; 347 done: 348 driver_env_fini(&env); 349 return rc; 350 }