|
 |
f779e3 |
/***************************************************************/
|
|
 |
f779e3 |
/* perk: PE Resource Kit */
|
|
 |
6a5301 |
/* Copyright (C) 2015--2025 SysDeer Technologies, LLC */
|
|
 |
f779e3 |
/* Released under GPLv2 and GPLv3; see COPYING.PERK. */
|
|
 |
f779e3 |
/***************************************************************/
|
|
 |
f779e3 |
|
|
 |
f779e3 |
#include <stdio.h>
|
|
 |
f779e3 |
#include <string.h>
|
|
 |
c87d85 |
#include <inttypes.h>
|
|
 |
f779e3 |
|
|
 |
f779e3 |
#include <perk/perk.h>
|
|
 |
c87d85 |
#include <perk/perk_consts.h>
|
|
 |
d6e02b |
#include <perk/perk_structs.h>
|
|
 |
f779e3 |
#include <perk/perk_output.h>
|
|
 |
f779e3 |
#include "perk_reader_impl.h"
|
|
 |
67bba7 |
#include "perk_driver_impl.h"
|
|
 |
67bba7 |
#include "perk_dprintf_impl.h"
|
|
 |
f779e3 |
#include "perk_errinfo_impl.h"
|
|
 |
f779e3 |
|
|
 |
c87d85 |
#define PPRIX64 "%"PRIx64
|
|
 |
c87d85 |
|
|
 |
c87d85 |
static const char * pe_sym_type_desc_msb[0x10] = {
|
|
 |
c87d85 |
[PE_IMAGE_SYM_DTYPE_NULL] = "scalar variable, ",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_DTYPE_POINTER] = "pointer to ",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_DTYPE_FUNCTION] = "function returning ",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_DTYPE_ARRAY] = "array of ",
|
|
 |
c87d85 |
};
|
|
 |
c87d85 |
|
|
 |
c87d85 |
static const char * pe_sym_type_desc_lsb[0x10] = {
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_NULL] = "unknown type",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_VOID] = "void",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_CHAR] = "char",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_SHORT] = "short",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_INT] = "int",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_LONG] = "long",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_FLOAT] = "float",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_DOUBLE] = "double",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_STRUCT] = "struct",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_UNION] = "union",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_ENUM] = "enum",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_MOE] = "enum member",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_BYTE] = "byte",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_WORD] = "word",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_UINT] = "uint",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_TYPE_DWORD] = "dword",
|
|
 |
c87d85 |
};
|
|
 |
c87d85 |
|
|
 |
c87d85 |
static const char * pe_sym_storage_class_desc[256] = {
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_NULL] = "storage class not assigned",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_AUTOMATIC] = "automatic (stack) variable",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_EXTERNAL] = "external symbol",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_STATIC] = "static variable",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_REGISTER] = "register variable",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_EXTERNAL_DEF] = "externally defined variable",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_LABEL] = "label",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_UNDEFINED_LABEL] = "undefined label",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_MEMBER_OF_STRUCT] = "struct member",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_ARGUMENT] = "formal function parameter",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_STRUCT_TAG] = "struct tag",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_MEMBER_OF_UNION] = "union member",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_UNION_TAG] = "union tag",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_TYPE_DEFINITION] = "type definition",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_UNDEFINED_STATIC] = "undefined (.bss) static data",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_ENUM_TAG] = "enum tag",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_MEMBER_OF_ENUM] = "enum member",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_REGISTER_PARAM] = "register parameter",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_BIT_FIELD] = "bit field",
|
|
 |
c87d85 |
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_BLOCK] = "a beginning-of-block or end-of-block record",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_FUNCTION] = "function record",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_END_OF_STRUCT] = "end-of-struct",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_FILE] = "file",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_SECTION] = "section definition",
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_WEAK_EXTERN] = "weak external",
|
|
 |
c87d85 |
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_CLR_TOKEN] = "CLR token",
|
|
 |
c87d85 |
|
|
 |
c87d85 |
[PE_IMAGE_SYM_CLASS_END_OF_FUNC] = "end-of-function"
|
|
 |
c87d85 |
};
|
|
 |
c87d85 |
|
|
 |
cacb6a |
static const char * pe_weak_extern_switches[4] = {
|
|
 |
cacb6a |
[PE_IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY] = "do not perform a library search",
|
|
 |
cacb6a |
[PE_IMAGE_WEAK_EXTERN_SEARCH_LIBRARY] = "do perform a library search",
|
|
 |
cacb6a |
[PE_IMAGE_WEAK_EXTERN_SEARCH_ALIAS] = "apply weak alias semantics",
|
|
 |
cacb6a |
};
|
|
 |
cacb6a |
|
|
 |
99be98 |
static const char * pe_comdat_select_desc[0x7] = {
|
|
 |
99be98 |
[PE_IMAGE_COMDAT_SELECT_NODUPLICATES] = "PE_IMAGE_COMDAT_SELECT_NODUPLICATES",
|
|
 |
99be98 |
[PE_IMAGE_COMDAT_SELECT_ANY] = "PE_IMAGE_COMDAT_SELECT_ANY",
|
|
 |
99be98 |
[PE_IMAGE_COMDAT_SELECT_SAME_SIZE] = "PE_IMAGE_COMDAT_SELECT_SAME_SIZE",
|
|
 |
99be98 |
[PE_IMAGE_COMDAT_SELECT_EXACT_MATCH] = "PE_IMAGE_COMDAT_SELECT_EXACT_MATCH",
|
|
 |
99be98 |
[PE_IMAGE_COMDAT_SELECT_ASSOCIATIVE] = "PE_IMAGE_COMDAT_SELECT_ASSOCIATIVE",
|
|
 |
99be98 |
[PE_IMAGE_COMDAT_SELECT_LARGEST] = "PE_IMAGE_COMDAT_SELECT_LARGEST",
|
|
 |
99be98 |
};
|
|
 |
cacb6a |
|
|
 |
ece10a |
static int pe_output_symbol_names(
|
|
 |
f779e3 |
const struct pe_driver_ctx * dctx,
|
|
 |
ece10a |
const struct pe_image_meta * meta,
|
|
 |
ece10a |
int fdout)
|
|
 |
f779e3 |
{
|
|
 |
f848fd |
struct pe_meta_coff_symbol * symrec;
|
|
 |
f779e3 |
|
|
 |
ece10a |
for (symrec=meta->m_symtbl; symrec->cs_name; symrec++)
|
|
 |
ece10a |
if (pe_dprintf(fdout,"%s\n",symrec->cs_name) < 0)
|
|
 |
f779e3 |
return PERK_FILE_ERROR(dctx);
|
|
 |
f779e3 |
|
|
 |
ece10a |
return 0;
|
|
 |
ece10a |
}
|
|
 |
ece10a |
|
|
 |
ece10a |
static int pe_output_symbol_names_yaml(
|
|
 |
ece10a |
const struct pe_driver_ctx * dctx,
|
|
 |
ece10a |
const struct pe_image_meta * meta,
|
|
 |
ece10a |
int fdout)
|
|
 |
ece10a |
{
|
|
 |
ece10a |
struct pe_meta_coff_symbol * symrec;
|
|
 |
ece10a |
|
|
 |
ece10a |
if (pe_dprintf(fdout," - Symbols:\n") < 0)
|
|
 |
ece10a |
return PERK_FILE_ERROR(dctx);
|
|
 |
f779e3 |
|
|
 |
f848fd |
for (symrec=meta->m_symtbl; symrec->cs_name; symrec++)
|
|
 |
ece10a |
if (pe_dprintf(fdout," - [ symbol: %s ]\n",symrec->cs_name) < 0)
|
|
 |
ea6b78 |
return PERK_FILE_ERROR(dctx);
|
|
 |
f779e3 |
|
|
 |
f779e3 |
return 0;
|
|
 |
f779e3 |
}
|
|
 |
ece10a |
|
|
 |
c87d85 |
static int pe_output_symbol_records_yaml(
|
|
 |
c87d85 |
const struct pe_driver_ctx * dctx,
|
|
 |
c87d85 |
const struct pe_image_meta * meta,
|
|
 |
c87d85 |
int fdout)
|
|
 |
c87d85 |
{
|
|
 |
c87d85 |
struct pe_meta_coff_symbol * symrec;
|
|
 |
c87d85 |
struct pe_meta_sec_hdr * sechdr;
|
|
 |
c87d85 |
const char * secname;
|
|
 |
c87d85 |
const char * classdesc;
|
|
 |
c87d85 |
const char * typedesc[2];
|
|
 |
c87d85 |
|
|
 |
c87d85 |
if (pe_dprintf(fdout," - Symbols:\n") < 0)
|
|
 |
c87d85 |
return PERK_FILE_ERROR(dctx);
|
|
 |
c87d85 |
|
|
 |
c87d85 |
for (symrec=meta->m_symtbl; symrec->cs_name; symrec++) {
|
|
 |
c87d85 |
switch (symrec->cs_section_number) {
|
|
 |
c87d85 |
case PE_IMAGE_SYM_UNDEFINED:
|
|
 |
c87d85 |
secname = "NULL (section is not yet defined)";
|
|
 |
c87d85 |
break;
|
|
 |
c87d85 |
|
|
 |
c87d85 |
case PE_IMAGE_SYM_ABSOLUTE:
|
|
 |
c87d85 |
secname = "N/A (symbol is an absolute value)";
|
|
 |
c87d85 |
break;
|
|
 |
c87d85 |
|
|
 |
c87d85 |
case PE_IMAGE_SYM_DEBUG:
|
|
 |
c87d85 |
secname = "N/A (debug symbol only)";
|
|
 |
c87d85 |
break;
|
|
 |
c87d85 |
|
|
 |
c87d85 |
default:
|
|
 |
c87d85 |
sechdr = &meta->m_sectbl[symrec->cs_section_number - 1];
|
|
 |
39eebb |
secname = sechdr->sh_name;
|
|
 |
c87d85 |
}
|
|
 |
c87d85 |
|
|
 |
c87d85 |
typedesc[0] = pe_sym_type_desc_msb[(symrec->cs_type >> 4) & 0x03];
|
|
 |
c87d85 |
typedesc[1] = pe_sym_type_desc_lsb[(symrec->cs_type >> 0) & 0x0f];
|
|
 |
c87d85 |
|
|
 |
c87d85 |
if (!(classdesc = pe_sym_storage_class_desc[symrec->cs_storage_class]))
|
|
 |
c87d85 |
classdesc = "UNRECOGNIZED DATA";
|
|
 |
c87d85 |
|
|
 |
c87d85 |
if (pe_dprintf(fdout,
|
|
 |
c87d85 |
" - symbol:\n"
|
|
 |
c87d85 |
" - [ name: %s ]\n"
|
|
 |
c87d85 |
" - [ value: 0x%08x ]\n"
|
|
 |
c87d85 |
" - [ crc32: 0x%08x ]\n"
|
|
 |
c87d85 |
" - [ crc64: 0x"PPRIX64" ]\n"
|
|
 |
99be98 |
" - [ secnum: %d (one-based) ]\n"
|
|
 |
c87d85 |
" - [ secname: %s ]\n"
|
|
 |
c87d85 |
" - [ type: 0x%02X ]\n"
|
|
 |
c87d85 |
" - [ typedesc: %s%s ]\n"
|
|
 |
c87d85 |
" - [ class: %d ]\n"
|
|
 |
c87d85 |
" - [ classname: %s ]\n"
|
|
 |
c87d85 |
" - [ aux-recs: %d ]\n"
|
|
 |
c87d85 |
"\n",
|
|
 |
c87d85 |
symrec->cs_name,
|
|
 |
c87d85 |
symrec->cs_value,
|
|
 |
c87d85 |
symrec->cs_crc32,
|
|
 |
c87d85 |
symrec->cs_crc64,
|
|
 |
c87d85 |
symrec->cs_section_number,
|
|
 |
c87d85 |
secname,
|
|
 |
c87d85 |
symrec->cs_type,
|
|
 |
c87d85 |
typedesc[0],typedesc[1],
|
|
 |
c87d85 |
symrec->cs_storage_class,
|
|
 |
c87d85 |
classdesc,
|
|
 |
c87d85 |
symrec->cs_num_of_aux_recs) < 0)
|
|
 |
c87d85 |
return PERK_FILE_ERROR(dctx);
|
|
 |
4816ec |
|
|
 |
4816ec |
if (symrec->cs_storage_class == PE_IMAGE_SYM_CLASS_WEAK_EXTERN) {
|
|
 |
4816ec |
const struct pe_raw_coff_symbol * coffsym;
|
|
 |
4816ec |
struct pe_meta_aux_rec_weaksym auxrec;
|
|
 |
4816ec |
int idx;
|
|
 |
4816ec |
|
|
 |
4816ec |
coffsym = (const struct pe_raw_coff_symbol *)symrec->cs_aux_recs;
|
|
 |
4816ec |
coffsym--;
|
|
 |
4816ec |
|
|
 |
4816ec |
if (pe_dprintf(fdout," - aux-rec:\n") < 0)
|
|
 |
4816ec |
return PERK_SYSTEM_ERROR(dctx);
|
|
 |
4816ec |
|
|
 |
4816ec |
for (idx=0; idx<symrec->cs_num_of_aux_recs; idx++) {
|
|
 |
4816ec |
pe_read_aux_rec_weaksym(coffsym,&auxrec,idx);
|
|
 |
4816ec |
|
|
 |
4816ec |
if (pe_dprintf(fdout,
|
|
 |
73d2ea |
" - [ tag-index: %d ]\n"
|
|
 |
73d2ea |
" - [ tag-characteristics: 0x%01X (%s) ]\n\n",
|
|
 |
4816ec |
auxrec.aux_tag_index,
|
|
 |
cacb6a |
auxrec.aux_characteristics,
|
|
 |
cacb6a |
pe_weak_extern_switches[auxrec.aux_characteristics & 0x03]) < 0)
|
|
 |
4816ec |
return PERK_SYSTEM_ERROR(dctx);
|
|
 |
4816ec |
}
|
|
 |
afa3a7 |
|
|
 |
afa3a7 |
} else if (!strcmp(symrec->cs_name,secname)) {
|
|
 |
afa3a7 |
const struct pe_raw_coff_symbol * coffsym;
|
|
 |
afa3a7 |
struct pe_meta_aux_rec_section auxrec;
|
|
 |
afa3a7 |
int idx;
|
|
 |
99be98 |
char selection[64];
|
|
 |
99be98 |
const int seldesclo = PE_IMAGE_COMDAT_SELECT_NODUPLICATES;
|
|
 |
99be98 |
const int seldeschi = PE_IMAGE_COMDAT_SELECT_LARGEST;
|
|
 |
afa3a7 |
|
|
 |
afa3a7 |
coffsym = (const struct pe_raw_coff_symbol *)symrec->cs_aux_recs;
|
|
 |
afa3a7 |
coffsym--;
|
|
 |
afa3a7 |
|
|
 |
afa3a7 |
if (pe_dprintf(fdout," - aux-rec:\n") < 0)
|
|
 |
afa3a7 |
return PERK_SYSTEM_ERROR(dctx);
|
|
 |
afa3a7 |
|
|
 |
afa3a7 |
for (idx=0; idx<symrec->cs_num_of_aux_recs; idx++) {
|
|
 |
afa3a7 |
pe_read_aux_rec_section(coffsym,&auxrec,idx);
|
|
 |
afa3a7 |
|
|
 |
99be98 |
if ((auxrec.aux_selection >= seldesclo) && (auxrec.aux_selection <= seldeschi)) {
|
|
 |
99be98 |
snprintf(selection,sizeof(selection),"%u (%s)",
|
|
 |
99be98 |
auxrec.aux_selection,
|
|
 |
99be98 |
pe_comdat_select_desc[auxrec.aux_selection]);
|
|
 |
99be98 |
} else {
|
|
 |
99be98 |
snprintf(selection,sizeof(selection),"%u",
|
|
 |
99be98 |
auxrec.aux_selection);
|
|
 |
99be98 |
}
|
|
 |
99be98 |
|
|
 |
afa3a7 |
if (pe_dprintf(fdout,
|
|
 |
73d2ea |
" - [ size: 0x%08x ]\n"
|
|
 |
73d2ea |
" - [ num-of-relocs: 0x%08X ]\n"
|
|
 |
73d2ea |
" - [ num-of-line-nums: 0x%08X ]\n"
|
|
 |
73d2ea |
" - [ check-sum: 0x%08X ]\n"
|
|
 |
73d2ea |
" - [ number: %u ]\n"
|
|
 |
99be98 |
" - [ selection: %s ]\n"
|
|
 |
afa3a7 |
"\n",
|
|
 |
afa3a7 |
auxrec.aux_size,
|
|
 |
afa3a7 |
auxrec.aux_num_of_relocs,
|
|
 |
afa3a7 |
auxrec.aux_num_of_line_nums,
|
|
 |
afa3a7 |
auxrec.aux_check_sum,
|
|
 |
afa3a7 |
auxrec.aux_number,
|
|
 |
99be98 |
selection) < 0)
|
|
 |
afa3a7 |
return PERK_SYSTEM_ERROR(dctx);
|
|
 |
afa3a7 |
}
|
|
 |
4816ec |
}
|
|
 |
c87d85 |
}
|
|
 |
c87d85 |
|
|
 |
c87d85 |
return 0;
|
|
 |
c87d85 |
}
|
|
 |
c87d85 |
|
|
 |
f1811f |
static int pe_output_pecoff_symbols_yaml(
|
|
 |
c87d85 |
const struct pe_driver_ctx * dctx,
|
|
 |
c87d85 |
const struct pe_image_meta * meta,
|
|
 |
c87d85 |
int fdout)
|
|
 |
c87d85 |
{
|
|
 |
c87d85 |
if (dctx->cctx->fmtflags & PERK_PRETTY_VERBOSE) {
|
|
 |
c87d85 |
if (pe_output_symbol_records_yaml(dctx,meta,fdout) < 0)
|
|
 |
c87d85 |
return PERK_NESTED_ERROR(dctx);
|
|
 |
c87d85 |
} else {
|
|
 |
c87d85 |
if (pe_output_symbol_names_yaml(dctx,meta,fdout) < 0)
|
|
 |
c87d85 |
return PERK_NESTED_ERROR(dctx);
|
|
 |
c87d85 |
}
|
|
 |
c87d85 |
|
|
 |
c87d85 |
return 0;
|
|
 |
c87d85 |
}
|
|
 |
c87d85 |
|
|
 |
f1811f |
int pe_output_pecoff_symbols(
|
|
 |
ece10a |
const struct pe_driver_ctx * dctx,
|
|
 |
ece10a |
const struct pe_image_meta * meta)
|
|
 |
ece10a |
{
|
|
 |
ece10a |
int fdout = pe_driver_fdout(dctx);
|
|
 |
ece10a |
|
|
 |
0044bc |
if (!meta->m_symtbl)
|
|
 |
0044bc |
return 0;
|
|
 |
0044bc |
|
|
 |
ece10a |
if (dctx->cctx->fmtflags & PERK_PRETTY_YAML) {
|
|
 |
f1811f |
if (pe_output_pecoff_symbols_yaml(dctx,meta,fdout) < 0)
|
|
 |
ece10a |
return PERK_NESTED_ERROR(dctx);
|
|
 |
ece10a |
|
|
 |
ece10a |
} else {
|
|
 |
ece10a |
if (pe_output_symbol_names(dctx,meta,fdout) < 0)
|
|
 |
ece10a |
return PERK_NESTED_ERROR(dctx);
|
|
 |
ece10a |
}
|
|
 |
ece10a |
|
|
 |
ece10a |
return 0;
|
|
 |
ece10a |
}
|