diff --git a/libs/sccf/src/sccf_builder.c b/libs/sccf/src/sccf_builder.c index 5386b6c..c15d78d 100644 --- a/libs/sccf/src/sccf_builder.c +++ b/libs/sccf/src/sccf_builder.c @@ -64,6 +64,16 @@ void sccf_builder_add_section(sccf_builder_t *builder, scc_vec_push(builder->sccf.sect_datas, *sect_data); } +static inline usize sccf_sect_by_type(const sccf_t *sccf, sccf_enum_t type) { + const sccf_header_t *hdr = &sccf->header; + for (usize i = 0; i < (usize)hdr->sect_header_num; ++i) { + sccf_sect_header_t *sh = &scc_vec_at(sccf->sect_headers, i); + if (sh->sccf_sect_type == type) + return i; + } + return (usize)hdr->sect_header_num; +} + const sccf_t *sccf_builder_to_sccf(sccf_builder_t *builder) { if (builder->entry_symbol_name == nullptr) { builder->sccf.header.entry_point = 0; @@ -81,7 +91,9 @@ const sccf_t *sccf_builder_to_sccf(sccf_builder_t *builder) { } sccf_sect_header_t sect_header; - if (scc_vec_size(builder->strtab)) { + if (scc_vec_size(builder->strtab) && + sccf_sect_by_type(&builder->sccf, SCCF_SECT_STRTAB) == + builder->sccf.header.sect_header_num) { sect_header = (sccf_sect_header_t){ .name = ".strtab", .info = 0, @@ -94,7 +106,9 @@ const sccf_t *sccf_builder_to_sccf(sccf_builder_t *builder) { (void *)&builder->strtab); } - if (scc_vec_size(builder->symtab)) { + if (scc_vec_size(builder->symtab) && + sccf_sect_by_type(&builder->sccf, SCCF_SECT_SYMTAB) == + builder->sccf.header.sect_header_num) { sect_header = (sccf_sect_header_t){ .name = ".symtab", .info = 0, @@ -107,7 +121,9 @@ const sccf_t *sccf_builder_to_sccf(sccf_builder_t *builder) { (void *)&builder->symtab); } - if (scc_vec_size(builder->relocs)) { + if (scc_vec_size(builder->relocs) && + sccf_sect_by_type(&builder->sccf, SCCF_SECT_RELOCS) == + builder->sccf.header.sect_header_num) { sect_header = (sccf_sect_header_t){ .name = ".relocs", .info = 0, diff --git a/src/bin/sccfdump.c b/src/bin/sccfdump.c new file mode 100644 index 0000000..07a77ed --- /dev/null +++ b/src/bin/sccfdump.c @@ -0,0 +1,336 @@ +#include +#include + +void init_platform(void); + +typedef struct { + const char *filepath; + const char *output; + cbool dump_header; + cbool dump_sections; + cbool dump_symbols; + cbool dump_relocs; + cbool dump_hex; + cbool dump_all; + int verbose; +} sccfdump_config_t; + +static const char *arch_name(sccf_enum_t arch) { + switch (arch) { + case SCCF_ARCH_UNKNOWN: return "UNKNOWN"; + case SCCF_ARCH_RISCV32: return "RISC-V 32"; + case SCCF_ARCH_RISCV64: return "RISC-V 64"; + case SCCF_ARCH_X86: return "x86"; + case SCCF_ARCH_AMD64: return "AMD64"; + default: return "UNKNOWN"; + } +} + +static const char *type_name(sccf_enum_t type) { + switch (type) { + case SCCF_TYPE_FLAG_EXECUTABLE: return "executable"; + case SCCF_TYPE_FLAG_RELOCATABLE: return "relocatable"; + case SCCF_TYPE_FLAG_EXE_RELOC: return "exe+reloc"; + default: return "UNKNOWN"; + } +} + +static const char *sect_type_name(sccf_enum_t t) { + switch (t) { + case SCCF_SECT_NONE: return "NONE"; + case SCCF_SECT_CODE: return "CODE"; + case SCCF_SECT_DATA: return "DATA"; + case SCCF_SECT_RODATA: return "RODATA"; + case SCCF_SECT_UNINIT_DATA: return "BSS"; + case SCCF_SECT_SYMTAB: return "SYMTAB"; + case SCCF_SECT_STRTAB: return "STRTAB"; + case SCCF_SECT_RELOCS: return "RELOCS"; + default: return "UNKNOWN"; + } +} + +static const char *sym_type_name(sccf_enum_t t) { + switch (t) { + case SCCF_SYM_TYPE_UNDEF: return "UNDEF"; + case SCCF_SYM_TYPE_FUNC: return "FUNC"; + case SCCF_SYM_TYPE_DATA: return "DATA"; + case SCCF_SYM_TYPE_EXTERN: return "EXTERN"; + default: return "?"; + } +} + +static const char *sym_bind_name(sccf_enum_t b) { + switch (b) { + case SCCF_SYM_BIND_LOCAL: return "LOCAL"; + case SCCF_SYM_BIND_GLOBAL: return "GLOBAL"; + case SCCF_SYM_BIND_WEAK: return "WEAK"; + default: return "?"; + } +} + +static void dump_header(const sccf_header_t *hdr) { + scc_printf("header:\n"); + scc_printf(" magic: %c%c%c%c%c%c%c%c\n", + hdr->magic[0], hdr->magic[1], hdr->magic[2], hdr->magic[3], + hdr->magic[4], hdr->magic[5], hdr->magic[6], hdr->magic[7]); + scc_printf(" type: %u (%s)\n", hdr->type, type_name(hdr->type)); + scc_printf(" version: %u\n", hdr->version); + scc_printf(" arch: %u (%s)\n", hdr->arch, arch_name(hdr->arch)); + scc_printf(" entry: 0x%llx\n", (unsigned long long)hdr->entry_point); + scc_printf(" sect_count: %llu\n", (unsigned long long)hdr->sect_header_num); +} + +static void dump_sections(u8 *base) { + const sccf_header_t *hdr = (const sccf_header_t *)base; + scc_printf("sections:\n"); + for (usize i = 0; i < (usize)hdr->sect_header_num; i++) { + const sccf_sect_header_t *sh = sccf_sect_header(base, i); + if (!sh) break; + scc_printf(" [%-2llu] %-8s size=%-8llu data_sz=%-8llu align=%-8llu name=%.8s\n", + (unsigned long long)i, + sect_type_name(sh->sccf_sect_type), + (unsigned long long)sh->size, + (unsigned long long)sh->data_size, + (unsigned long long)sh->addralign, + sh->name); + } +} + +static void dump_symbols(u8 *base) { + usize sym_idx = sccf_find_sect_by_type(base, SCCF_SECT_SYMTAB); + if (sym_idx >= ((const sccf_header_t *)base)->sect_header_num) { + scc_printf("(no symbol table)\n"); + return; + } + usize str_idx = sccf_find_sect_by_type(base, SCCF_SECT_STRTAB); + const char *strtab = NULL; + if (str_idx < ((const sccf_header_t *)base)->sect_header_num) { + strtab = (const char *)sccf_sect_data(base, str_idx); + } + + const sccf_sect_header_t *sh = sccf_sect_header(base, sym_idx); + usize count = sh->size / sizeof(sccf_sym_t); + const sccf_sym_t *syms = (const sccf_sym_t *)sccf_sect_data(base, sym_idx); + + scc_printf("symbols:\n"); + for (usize i = 0; i < count; i++) { + const char *name = strtab ? strtab + syms[i].name_offset : ""; + scc_printf(" [%-2llu] %-8s %-6s %-8s 0x%-6llx %s\n", + (unsigned long long)i, + sym_type_name(syms[i].sccf_sym_type), + sym_bind_name(syms[i].sccf_sym_bind), + sect_type_name(syms[i].sccf_sect_type), + (unsigned long long)syms[i].sccf_sect_offset, + name); + } +} + +static void dump_relocs(u8 *base) { + usize reloc_idx = sccf_find_sect_by_type(base, SCCF_SECT_RELOCS); + if (reloc_idx >= ((const sccf_header_t *)base)->sect_header_num) { + scc_printf("(no relocations)\n"); + return; + } + + const sccf_sect_header_t *sh = sccf_sect_header(base, reloc_idx); + usize count = sh->size / sizeof(sccf_reloc_t); + const sccf_reloc_t *relocs = (const sccf_reloc_t *)sccf_sect_data(base, reloc_idx); + + scc_printf("relocations:\n"); + for (usize i = 0; i < count; i++) { + scc_printf(" [%-2llu] type=%-3u sect=%-8s sym=%-2llu offset=0x%-6llx\n", + (unsigned long long)i, + relocs[i].reloc_type, + sect_type_name(relocs[i].sect_type), + (unsigned long long)relocs[i].sym_idx, + (unsigned long long)relocs[i].offset); + } +} + +static void dump_hex(u8 *base, int verbose) { + const sccf_header_t *hdr = (const sccf_header_t *)base; + for (usize i = 0; i < (usize)hdr->sect_header_num; i++) { + const sccf_sect_header_t *sh = sccf_sect_header(base, i); + if (!sh || sh->sccf_sect_type == SCCF_SECT_SYMTAB || + sh->sccf_sect_type == SCCF_SECT_STRTAB || + sh->sccf_sect_type == SCCF_SECT_RELOCS) + continue; + + u8 *data = sccf_sect_data(base, i); + if (!data || sh->size == 0) continue; + + scc_printf("section [%-2llu] %.8s (%llu bytes):\n", + (unsigned long long)i, sh->name, (unsigned long long)sh->size); + usize limit = verbose ? sh->size : (sh->size > 256 ? 256 : sh->size); + for (usize j = 0; j < limit; j++) { + if (j % 16 == 0) scc_printf(" %08llx: ", (unsigned long long)j); + scc_printf("%02x ", data[j]); + if ((j + 1) % 16 == 0 || j + 1 == limit) scc_printf("\n"); + } + if (limit < sh->size) + scc_printf(" ... (%llu more bytes)\n", + (unsigned long long)(sh->size - limit)); + } +} + +int main(int argc, const char **argv, const char **envp) { + init_platform(); + + enum { + HINT_PROG_NAME, + HINT_DESCRIPTION, + HINT_FILE, + HINT_OUTPUT, + HINT_HEADER, + HINT_SECTIONS, + HINT_SYMBOLS, + HINT_RELOCS, + HINT_HEX, + HINT_VERBOSE, + HINT_ALL, + }; + static const char *hints_en[] = { + [HINT_PROG_NAME] = "sccfdump", + [HINT_DESCRIPTION] = "SCCF file dump tool - inspect SCCF format files", + [HINT_FILE] = "SCCF file to inspect", + [HINT_OUTPUT] = "Output file (`-` means stdout)", + [HINT_HEADER] = "Dump file header", + [HINT_SECTIONS] = "Dump section headers", + [HINT_SYMBOLS] = "Dump symbol table", + [HINT_RELOCS] = "Dump relocation entries", + [HINT_HEX] = "Dump section hex content", + [HINT_VERBOSE] = "Verbose output (use with -x)", + [HINT_ALL] = "Dump header, sections, symbols and relocs", + }; + static const char *hints_zh[] = { + [HINT_PROG_NAME] = "sccfdump", + [HINT_DESCRIPTION] = "SCCF文件转存工具 - 查看SCCF格式文件", + [HINT_FILE] = "要查看的SCCF文件", + [HINT_OUTPUT] = "输出文件(`-`表示标准输出流)", + [HINT_HEADER] = "转存文件头", + [HINT_SECTIONS] = "转存段头表", + [HINT_SYMBOLS] = "转存符号表", + [HINT_RELOCS] = "转存重定位条目", + [HINT_HEX] = "转存段的十六进制内容", + [HINT_VERBOSE] = "详细输出(与-x配合使用)", + [HINT_ALL] = "转存文件头、段、符号和重定位", + }; + + scc_argparse_lang_t lang = SCC_ARGPARSE_LANG_EN; + for (const char **env = envp; *env; env++) { + if (scc_strcmp(*env, "LANG=zh_CN.UTF-8") == 0) { + lang = SCC_ARGPARSE_LANG_ZH; + break; + } + } + const char **hints = lang == SCC_ARGPARSE_LANG_ZH ? hints_zh : hints_en; + + sccfdump_config_t config; + scc_memset(&config, 0, sizeof(config)); + config.dump_all = true; + + scc_argparse_t argparse; + scc_argparse_init(&argparse, hints[HINT_PROG_NAME], hints[HINT_DESCRIPTION]); + argparse.lang = lang; + + scc_argparse_cmd_t *root = scc_argparse_get_root(&argparse); + + scc_argparse_opt_t opt_output; + scc_argparse_opt_init(&opt_output, 'o', "output", hints[HINT_OUTPUT]); + scc_argparse_spec_setup_string(&opt_output.spec, &config.output); + scc_argparse_cmd_add_opt(root, &opt_output); + + scc_argparse_opt_t opt_all; + scc_argparse_opt_init(&opt_all, 'a', "all", hints[HINT_ALL]); + scc_argparse_spec_setup_bool(&opt_all.spec, &config.dump_all); + scc_argparse_cmd_add_opt(root, &opt_all); + + scc_argparse_opt_t opt_header; + scc_argparse_opt_init(&opt_header, 'H', "header", hints[HINT_HEADER]); + scc_argparse_spec_setup_bool(&opt_header.spec, &config.dump_header); + scc_argparse_cmd_add_opt(root, &opt_header); + + scc_argparse_opt_t opt_sections; + scc_argparse_opt_init(&opt_sections, 's', "sections", hints[HINT_SECTIONS]); + scc_argparse_spec_setup_bool(&opt_sections.spec, &config.dump_sections); + scc_argparse_cmd_add_opt(root, &opt_sections); + + scc_argparse_opt_t opt_symbols; + scc_argparse_opt_init(&opt_symbols, 'S', "symbols", hints[HINT_SYMBOLS]); + scc_argparse_spec_setup_bool(&opt_symbols.spec, &config.dump_symbols); + scc_argparse_cmd_add_opt(root, &opt_symbols); + + scc_argparse_opt_t opt_relocs; + scc_argparse_opt_init(&opt_relocs, 'r', "relocs", hints[HINT_RELOCS]); + scc_argparse_spec_setup_bool(&opt_relocs.spec, &config.dump_relocs); + scc_argparse_cmd_add_opt(root, &opt_relocs); + + scc_argparse_opt_t opt_hex; + scc_argparse_opt_init(&opt_hex, 'x', "hex", hints[HINT_HEX]); + scc_argparse_spec_setup_bool(&opt_hex.spec, &config.dump_hex); + scc_argparse_cmd_add_opt(root, &opt_hex); + + scc_argparse_opt_t opt_verbose; + scc_argparse_opt_init(&opt_verbose, 'V', "verbose", hints[HINT_VERBOSE]); + scc_argparse_spec_setup_count(&opt_verbose.spec, &config.verbose); + scc_argparse_cmd_add_opt(root, &opt_verbose); + + scc_argparse_arg_t arg_file; + scc_argparse_arg_init(&arg_file, "file", hints[HINT_FILE]); + scc_argparse_spec_setup_string(&arg_file.spec, &config.filepath); + scc_argparse_spec_set_required(&arg_file.spec, true); + scc_argparse_cmd_add_arg(root, &arg_file); + + int ret = scc_argparse_parse(&argparse, argc, argv); + if (ret != 0) { + scc_argparse_drop(&argparse); + return 0; + } + scc_argparse_drop(&argparse); + + scc_file_t fp = scc_fopen(config.filepath, SCC_FILE_READ); + if (!fp) { + LOG_ERROR("cannot open %s", config.filepath); + return 1; + } + + usize fsize = scc_fsize(fp); + if (fsize < sizeof(sccf_header_t)) { + LOG_ERROR("file too small: %s (%llu bytes)", config.filepath, + (unsigned long long)fsize); + scc_fclose(fp); + return 1; + } + + sccf_buffer_t buf; + scc_vec_init(buf); + scc_vec_realloc(buf, fsize); + usize nread = scc_fread(fp, scc_vec_unsafe_get_data(buf), fsize); + scc_fclose(fp); + if (nread != fsize) { + LOG_ERROR("short read %s", config.filepath); + scc_vec_free(buf); + return 1; + } + scc_vec_size(buf) = fsize; + + sccf_t sccf; + sccf_parse(&sccf, &buf, 0); + u8 *base = scc_vec_unsafe_get_data(buf); + + if (config.dump_all) { + dump_header(&sccf.header); + dump_sections(base); + dump_symbols(base); + dump_relocs(base); + } else { + if (config.dump_header) dump_header(&sccf.header); + if (config.dump_sections) dump_sections(base); + if (config.dump_symbols) dump_symbols(base); + if (config.dump_relocs) dump_relocs(base); + } + if (config.dump_hex) dump_hex(base, config.verbose); + + scc_vec_free(buf); + return 0; +} diff --git a/src/main.c b/src/main.c index 817b590..6860d8b 100644 --- a/src/main.c +++ b/src/main.c @@ -330,9 +330,9 @@ sstream_drop: config.entry_point_symbol); const sccf_t *sccf = sccf_builder_to_sccf(&sccf_builder); if (config.emit_stage == SCC_EMIT_STAGE_SCCF) { - sccf_buffer_t buffer; - scc_vec_init(buffer); - sccf_builder_to_buffer(&sccf_builder, &buffer); + // sccf_buffer_t buffer; + // scc_vec_init(buffer); + // sccf_builder_to_buffer(&sccf_builder, &buffer); if (fp == nullptr) { scc_printf("output exe at %s\n", config.output_file); } else {