/* SPDX-License-Identifier: GPL-2.0 *//* * Convert sample address to data type using DWARF debug info. * * Written by Namhyung Kim <namhyung@kernel.org> */ #include <stdio.h>#include <stdlib.h>#include <inttypes.h>#include <linux/zalloc.h> #include "annotate.h"#include "annotate-data.h"#include "debuginfo.h"#include "debug.h"#include "dso.h"#include "dwarf-regs.h"#include "evsel.h"#include "evlist.h"#include "map.h"#include "map_symbol.h"#include "sort.h"#include "strbuf.h"#include "symbol.h"#include "symbol_conf.h"#include "thread.h" /* register number of the stack pointer */#define X86_REG_SP 7 static void delete_var_types(struct die_var_type *var_types); enum type_state_kind { TSR_KIND_INVALID = 0, TSR_KIND_TYPE, TSR_KIND_PERCPU_BASE, TSR_KIND_CONST, TSR_KIND_POINTER, TSR_KIND_CANARY,}; #define pr_debug_dtp(fmt, ...) \do { \ if (debug_type_profile) \ pr_info(fmt, ##__VA_ARGS__); \ else \ pr_debug3(fmt, ##__VA_ARGS__); \} while (0) static void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind){ struct strbuf sb; char *str; Dwarf_Word size = 0; if (!debug_type_profile && verbose < 3) return; switch (kind) { case TSR_KIND_INVALID: pr_info("\n"); return; case TSR_KIND_PERCPU_BASE: pr_info(" percpu base\n"); return; case TSR_KIND_CONST: pr_info(" constant\n"); return; case TSR_KIND_POINTER: pr_info(" pointer"); /* it also prints the type info */ break; case TSR_KIND_CANARY: pr_info(" stack canary\n"); return; case TSR_KIND_TYPE: default: break; } dwarf_aggregate_size(die, &size); strbuf_init(&sb, 32); die_get_typename_from_type(die, &sb); str = strbuf_detach(&sb, NULL); pr_info(" type='%s' size=%#lx (die:%#lx)\n", str, (long)size, (long)dwarf_dieoffset(die)); free(str);} static void pr_debug_location(Dwarf_Die *die, u64 pc, int reg){ ptrdiff_t off = 0; Dwarf_Attribute attr; Dwarf_Addr base, start, end; Dwarf_Op *ops; size_t nops; if (!debug_type_profile && verbose < 3) return; if (dwarf_attr(die, DW_AT_location, &attr) == NULL) return; while ((off = dwarf_getlocations(&attr, off, &base, &start, &end, &ops, &nops)) > 0) { if (reg != DWARF_REG_PC && end < pc) continue; if (reg != DWARF_REG_PC && start > pc) break; pr_info(" variable location: "); switch (ops->atom) { case DW_OP_reg0 ...DW_OP_reg31: pr_info("reg%d\n", ops->atom - DW_OP_reg0); break; case DW_OP_breg0 ...DW_OP_breg31: pr_info("base=reg%d, offset=%#lx\n", ops->atom - DW_OP_breg0, (long)ops->number); break; case DW_OP_regx: pr_info("reg%ld\n", (long)ops->number); break; case DW_OP_bregx: pr_info("base=reg%ld, offset=%#lx\n", (long)ops->number, (long)ops->number2); break; case DW_OP_fbreg: pr_info("use frame base, offset=%#lx\n", (long)ops->number); break; case DW_OP_addr: pr_info("address=%#lx\n", (long)ops->number); break; default: pr_info("unknown: code=%#x, number=%#lx\n", ops->atom, (long)ops->number); break; } break; }} /* * Type information in a register, valid when @ok is true. * The @caller_saved registers are invalidated after a function call. */struct type_state_reg { Dwarf_Die type; u32 imm_value; bool ok; bool caller_saved; u8 kind;}; /* Type information in a stack location, dynamically allocated */struct type_state_stack { struct list_head list; Dwarf_Die type; int offset; int size; bool compound; u8 kind;}; /* FIXME: This should be arch-dependent */#define TYPE_STATE_MAX_REGS 16 /* * State table to maintain type info in each register and stack location. * It'll be updated when new variable is allocated or type info is moved * to a new location (register or stack). As it'd be used with the * shortest path of basic blocks, it only maintains a single table. */struct type_state { /* state of general purpose registers */ struct type_state_reg regs[TYPE_STATE_MAX_REGS]; /* state of stack location */ struct list_head stack_vars; /* return value register */ int ret_reg; /* stack pointer register */ int stack_reg;}; static bool has_reg_type(struct type_state *state, int reg){ return (unsigned)reg < ARRAY_SIZE(state->regs);} static void init_type_state(struct type_state *state, struct arch *arch){ memset(state, 0, sizeof(*state)); INIT_LIST_HEAD(&state->stack_vars); if (arch__is(arch, "x86")) { state->regs[0].caller_saved = true; state->regs[1].caller_saved = true; state->regs[2].caller_saved = true; state->regs[4].caller_saved = true; state->regs[5].caller_saved = true; state->regs[8].caller_saved = true; state->regs[9].caller_saved = true; state->regs[10].caller_saved = true; state->regs[11].caller_saved = true; state->ret_reg = 0; state->stack_reg = X86_REG_SP; }} static void exit_type_state(struct type_state *state){ struct type_state_stack *stack, *tmp; list_for_each_entry_safe(stack, tmp, &state->stack_vars, list) { list_del(&stack->list); free(stack); }} /* * Compare type name and size to maintain them in a tree. * I'm not sure if DWARF would have information of a single type in many * different places (compilation units). If not, it could compare the * offset of the type entry in the .debug_info section. */static int data_type_cmp(const void *_key, const struct rb_node *node){ const struct annotated_data_type *key = _key; struct annotated_data_type *type; type = rb_entry(node, struct annotated_data_type, node); if (key->self.size != type->self.size) return key->self.size - type->self.size; return strcmp(key->self.type_name, type->self.type_name);} static bool data_type_less(struct rb_node *node_a, const struct rb_node *node_b){ struct annotated_data_type *a, *b; a = rb_entry(node_a, struct annotated_data_type, node); b = rb_entry(node_b, struct annotated_data_type, node); if (a->self.size != b->self.size) return a->self.size < b->self.size; return strcmp(a->self.type_name, b->self.type_name) < 0;} /* Recursively add new members for struct/union */static int __add_member_cb(Dwarf_Die *die, void *arg){ struct annotated_member *parent = arg; struct annotated_member *member; Dwarf_Die member_type, die_mem; Dwarf_Word size, loc; Dwarf_Attribute attr; struct strbuf sb; int tag; if (dwarf_tag(die) != DW_TAG_member) return DIE_FIND_CB_SIBLING; member = zalloc(sizeof(*member)); if (member == NULL) return DIE_FIND_CB_END; strbuf_init(&sb, 32); die_get_typename(die, &sb); die_get_real_type(die, &member_type); if (dwarf_aggregate_size(&member_type, &size) < 0) size = 0; if (!dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) loc = 0; else dwarf_formudata(&attr, &loc); member->type_name = strbuf_detach(&sb, NULL); /* member->var_name can be NULL */ if (dwarf_diename(die)) member->var_name = strdup(dwarf_diename(die)); member->size = size; member->offset = loc + parent->offset; INIT_LIST_HEAD(&member->children); list_add_tail(&member->node, &parent->children); tag = dwarf_tag(&member_type); switch (tag) { case DW_TAG_structure_type: case DW_TAG_union_type: die_find_child(&member_type, __add_member_cb, member, &die_mem); break; default: break; } return DIE_FIND_CB_SIBLING;} static void add_member_types(struct annotated_data_type *parent, Dwarf_Die *type){ Dwarf_Die die_mem; die_find_child(type, __add_member_cb, &parent->self, &die_mem);} static void delete_members(struct annotated_member *member){ struct annotated_member *child, *tmp; list_for_each_entry_safe(child, tmp, &member->children, node) { list_del(&child->node); delete_members(child); zfree(&child->type_name); zfree(&child->var_name); free(child); }} static struct annotated_data_type *dso__findnew_data_type(struct dso *dso, Dwarf_Die *type_die){ struct annotated_data_type *result = NULL; struct annotated_data_type key; struct rb_node *node; struct strbuf sb; char *type_name; Dwarf_Word size; strbuf_init(&sb, 32); if (die_get_typename_from_type(type_die, &sb) < 0) strbuf_add(&sb, "(unknown type)", 14); type_name = strbuf_detach(&sb, NULL); dwarf_aggregate_size(type_die, &size); /* Check existing nodes in dso->data_types tree */ key.self.type_name = type_name; key.self.size = size; node = rb_find(&key, dso__data_types(dso), data_type_cmp); if (node) { result = rb_entry(node, struct annotated_data_type, node); free(type_name); return result; } /* If not, add a new one */ result = zalloc(sizeof(*result)); if (result == NULL) { free(type_name); return NULL; } result->self.type_name = type_name; result->self.size = size; INIT_LIST_HEAD(&result->self.children); if (symbol_conf.annotate_data_member) add_member_types(result, type_die); rb_add(&result->node, dso__data_types(dso), data_type_less); return result;} static bool find_cu_die(struct debuginfo *di, u64 pc, Dwarf_Die *cu_die){ Dwarf_Off off, next_off; size_t header_size; if (dwarf_addrdie(di->dbg, pc, cu_die) != NULL) return cu_die; /* * There are some kernels don't have full aranges and contain only a few * aranges entries. Fallback to iterate all CU entries in .debug_info * in case it's missing. */ off = 0; while (dwarf_nextcu(di->dbg, off, &next_off, &header_size, NULL, NULL, NULL) == 0) { if (dwarf_offdie(di->dbg, off + header_size, cu_die) && dwarf_haspc(cu_die, pc)) return true; off = next_off; } return false;} /* The type info will be saved in @type_die */static int check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, Dwarf_Die *type_die, int reg, int offset, bool is_fbreg){ Dwarf_Word size; bool is_pointer = true; if (reg == DWARF_REG_PC) is_pointer = false; else if (reg == dloc->fbreg || is_fbreg) is_pointer = false; else if (arch__is(dloc->arch, "x86") && reg == X86_REG_SP) is_pointer = false; /* Get the type of the variable */ if (die_get_real_type(var_die, type_die) == NULL) { pr_debug_dtp("variable has no type\n"); ann_data_stat.no_typeinfo++; return -1; } /* * Usually it expects a pointer type for a memory access. * Convert to a real type it points to. But global variables * and local variables are accessed directly without a pointer. */ if (is_pointer) { if ((dwarf_tag(type_die) != DW_TAG_pointer_type && dwarf_tag(type_die) != DW_TAG_array_type) || die_get_real_type(type_die, type_die) == NULL) { pr_debug_dtp("no pointer or no type\n"); ann_data_stat.no_typeinfo++; return -1; } } /* Get the size of the actual type */ if (dwarf_aggregate_size(type_die, &size) < 0) { pr_debug_dtp("type size is unknown\n"); ann_data_stat.invalid_size++; return -1; } /* Minimal sanity check */ if ((unsigned)offset >= size) { pr_debug_dtp("offset: %d is bigger than size: %"PRIu64"\n", offset, size); ann_data_stat.bad_offset++; return -1; } return 0;} static struct type_state_stack *find_stack_state(struct type_state *state, int offset){ struct type_state_stack *stack; list_for_each_entry(stack, &state->stack_vars, list) { if (offset == stack->offset) return stack; if (stack->compound && stack->offset < offset && offset < stack->offset + stack->size) return stack; } return NULL;} static void set_stack_state(struct type_state_stack *stack, int offset, u8 kind, Dwarf_Die *type_die){ int tag; Dwarf_Word size; if (dwarf_aggregate_size(type_die, &size) < 0) size = 0; tag = dwarf_tag(type_die); stack->type = *type_die; stack->size = size; stack->offset = offset; stack->kind = kind; switch (tag) { case DW_TAG_structure_type: case DW_TAG_union_type: stack->compound = (kind != TSR_KIND_POINTER); break; default: stack->compound = false; break; }} static struct type_state_stack *findnew_stack_state(struct type_state *state, int offset, u8 kind, Dwarf_Die *type_die){ struct type_state_stack *stack = find_stack_state(state, offset); if (stack) { set_stack_state(stack, offset, kind, type_die); return stack; } stack = malloc(sizeof(*stack)); if (stack) { set_stack_state(stack, offset, kind, type_die);Lines 1–500 of 2002. Load more
/* SPDX-License-Identifier: GPL-2.0 */ /* * Convert sample address to data type using DWARF debug info. * * Written by Namhyung Kim <namhyung@kernel.org> */ #include <stdio.h> #include <stdlib.h> #include <inttypes.h> #include <linux/zalloc.h> #include "annotate.h" #include "annotate-data.h" #include "debuginfo.h" #include "debug.h" #include "dso.h" #include "dwarf-regs.h" #include "evsel.h" #include "evlist.h" #include "map.h" #include "map_symbol.h" #include "sort.h" #include "strbuf.h" #include "symbol.h" #include "symbol_conf.h" #include "thread.h" /* register number of the stack pointer */ #define X86_REG_SP 7 static void delete_var_types(struct die_var_type *var_types); enum type_state_kind { TSR_KIND_INVALID = 0, TSR_KIND_TYPE, TSR_KIND_PERCPU_BASE, TSR_KIND_CONST, TSR_KIND_POINTER, TSR_KIND_CANARY, }; #define pr_debug_dtp(fmt, ...) \ do { \ if (debug_type_profile) \ pr_info(fmt, ##__VA_ARGS__); \ else \ pr_debug3(fmt, ##__VA_ARGS__); \ } while (0) static void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) { struct strbuf sb; char *str; Dwarf_Word size = 0; if (!debug_type_profile && verbose < 3) return; switch (kind) { case TSR_KIND_INVALID: pr_info("\n"); return; case TSR_KIND_PERCPU_BASE: pr_info(" percpu base\n"); return; case TSR_KIND_CONST: pr_info(" constant\n"); return; case TSR_KIND_POINTER: pr_info(" pointer"); /* it also prints the type info */ break; case TSR_KIND_CANARY: pr_info(" stack canary\n"); return; case TSR_KIND_TYPE: default: break; } dwarf_aggregate_size(die, &size); strbuf_init(&sb, 32); die_get_typename_from_type(die, &sb); str = strbuf_detach(&sb, NULL); pr_info(" type='%s' size=%#lx (die:%#lx)\n", str, (long)size, (long)dwarf_dieoffset(die)); free(str); } static void pr_debug_location(Dwarf_Die *die, u64 pc, int reg) { ptrdiff_t off = 0; Dwarf_Attribute attr; Dwarf_Addr base, start, end; Dwarf_Op *ops; size_t nops; if (!debug_type_profile && verbose < 3) return; if (dwarf_attr(die, DW_AT_location, &attr) == NULL) return; while ((off = dwarf_getlocations(&attr, off, &base, &start, &end, &ops, &nops)) > 0) { if (reg != DWARF_REG_PC && end < pc) continue; if (reg != DWARF_REG_PC && start > pc) break; pr_info(" variable location: "); switch (ops->atom) { case DW_OP_reg0 ...DW_OP_reg31: pr_info("reg%d\n", ops->atom - DW_OP_reg0); break; case DW_OP_breg0 ...DW_OP_breg31: pr_info("base=reg%d, offset=%#lx\n", ops->atom - DW_OP_breg0, (long)ops->number); break; case DW_OP_regx: pr_info("reg%ld\n", (long)ops->number); break; case DW_OP_bregx: pr_info("base=reg%ld, offset=%#lx\n", (long)ops->number, (long)ops->number2); break; case DW_OP_fbreg: pr_info("use frame base, offset=%#lx\n", (long)ops->number); break; case DW_OP_addr: pr_info("address=%#lx\n", (long)ops->number); break; default: pr_info("unknown: code=%#x, number=%#lx\n", ops->atom, (long)ops->number); break; } break; } } /* * Type information in a register, valid when @ok is true. * The @caller_saved registers are invalidated after a function call. */ struct type_state_reg { Dwarf_Die type; u32 imm_value; bool ok; bool caller_saved; u8 kind; }; /* Type information in a stack location, dynamically allocated */ struct type_state_stack { struct list_head list; Dwarf_Die type; int offset; int size; bool compound; u8 kind; }; /* FIXME: This should be arch-dependent */ #define TYPE_STATE_MAX_REGS 16 /* * State table to maintain type info in each register and stack location. * It'll be updated when new variable is allocated or type info is moved * to a new location (register or stack). As it'd be used with the * shortest path of basic blocks, it only maintains a single table. */ struct type_state { /* state of general purpose registers */ struct type_state_reg regs[TYPE_STATE_MAX_REGS]; /* state of stack location */ struct list_head stack_vars; /* return value register */ int ret_reg; /* stack pointer register */ int stack_reg; }; static bool has_reg_type(struct type_state *state, int reg) { return (unsigned)reg < ARRAY_SIZE(state->regs); } static void init_type_state(struct type_state *state, struct arch *arch) { memset(state, 0, sizeof(*state)); INIT_LIST_HEAD(&state->stack_vars); if (arch__is(arch, "x86")) { state->regs[0].caller_saved = true; state->regs[1].caller_saved = true; state->regs[2].caller_saved = true; state->regs[4].caller_saved = true; state->regs[5].caller_saved = true; state->regs[8].caller_saved = true; state->regs[9].caller_saved = true; state->regs[10].caller_saved = true; state->regs[11].caller_saved = true; state->ret_reg = 0; state->stack_reg = X86_REG_SP; } } static void exit_type_state(struct type_state *state) { struct type_state_stack *stack, *tmp; list_for_each_entry_safe(stack, tmp, &state->stack_vars, list) { list_del(&stack->list); free(stack); } } /* * Compare type name and size to maintain them in a tree. * I'm not sure if DWARF would have information of a single type in many * different places (compilation units). If not, it could compare the * offset of the type entry in the .debug_info section. */ static int data_type_cmp(const void *_key, const struct rb_node *node) { const struct annotated_data_type *key = _key; struct annotated_data_type *type; type = rb_entry(node, struct annotated_data_type, node); if (key->self.size != type->self.size) return key->self.size - type->self.size; return strcmp(key->self.type_name, type->self.type_name); } static bool data_type_less(struct rb_node *node_a, const struct rb_node *node_b) { struct annotated_data_type *a, *b; a = rb_entry(node_a, struct annotated_data_type, node); b = rb_entry(node_b, struct annotated_data_type, node); if (a->self.size != b->self.size) return a->self.size < b->self.size; return strcmp(a->self.type_name, b->self.type_name) < 0; } /* Recursively add new members for struct/union */ static int __add_member_cb(Dwarf_Die *die, void *arg) { struct annotated_member *parent = arg; struct annotated_member *member; Dwarf_Die member_type, die_mem; Dwarf_Word size, loc; Dwarf_Attribute attr; struct strbuf sb; int tag; if (dwarf_tag(die) != DW_TAG_member) return DIE_FIND_CB_SIBLING; member = zalloc(sizeof(*member)); if (member == NULL) return DIE_FIND_CB_END; strbuf_init(&sb, 32); die_get_typename(die, &sb); die_get_real_type(die, &member_type); if (dwarf_aggregate_size(&member_type, &size) < 0) size = 0; if (!dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) loc = 0; else dwarf_formudata(&attr, &loc); member->type_name = strbuf_detach(&sb, NULL); /* member->var_name can be NULL */ if (dwarf_diename(die)) member->var_name = strdup(dwarf_diename(die)); member->size = size; member->offset = loc + parent->offset; INIT_LIST_HEAD(&member->children); list_add_tail(&member->node, &parent->children); tag = dwarf_tag(&member_type); switch (tag) { case DW_TAG_structure_type: case DW_TAG_union_type: die_find_child(&member_type, __add_member_cb, member, &die_mem); break; default: break; } return DIE_FIND_CB_SIBLING; } static void add_member_types(struct annotated_data_type *parent, Dwarf_Die *type) { Dwarf_Die die_mem; die_find_child(type, __add_member_cb, &parent->self, &die_mem); } static void delete_members(struct annotated_member *member) { struct annotated_member *child, *tmp; list_for_each_entry_safe(child, tmp, &member->children, node) { list_del(&child->node); delete_members(child); zfree(&child->type_name); zfree(&child->var_name); free(child); } } static struct annotated_data_type *dso__findnew_data_type(struct dso *dso, Dwarf_Die *type_die) { struct annotated_data_type *result = NULL; struct annotated_data_type key; struct rb_node *node; struct strbuf sb; char *type_name; Dwarf_Word size; strbuf_init(&sb, 32); if (die_get_typename_from_type(type_die, &sb) < 0) strbuf_add(&sb, "(unknown type)", 14); type_name = strbuf_detach(&sb, NULL); dwarf_aggregate_size(type_die, &size); /* Check existing nodes in dso->data_types tree */ key.self.type_name = type_name; key.self.size = size; node = rb_find(&key, dso__data_types(dso), data_type_cmp); if (node) { result = rb_entry(node, struct annotated_data_type, node); free(type_name); return result; } /* If not, add a new one */ result = zalloc(sizeof(*result)); if (result == NULL) { free(type_name); return NULL; } result->self.type_name = type_name; result->self.size = size; INIT_LIST_HEAD(&result->self.children); if (symbol_conf.annotate_data_member) add_member_types(result, type_die); rb_add(&result->node, dso__data_types(dso), data_type_less); return result; } static bool find_cu_die(struct debuginfo *di, u64 pc, Dwarf_Die *cu_die) { Dwarf_Off off, next_off; size_t header_size; if (dwarf_addrdie(di->dbg, pc, cu_die) != NULL) return cu_die; /* * There are some kernels don't have full aranges and contain only a few * aranges entries. Fallback to iterate all CU entries in .debug_info * in case it's missing. */ off = 0; while (dwarf_nextcu(di->dbg, off, &next_off, &header_size, NULL, NULL, NULL) == 0) { if (dwarf_offdie(di->dbg, off + header_size, cu_die) && dwarf_haspc(cu_die, pc)) return true; off = next_off; } return false; } /* The type info will be saved in @type_die */ static int check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, Dwarf_Die *type_die, int reg, int offset, bool is_fbreg) { Dwarf_Word size; bool is_pointer = true; if (reg == DWARF_REG_PC) is_pointer = false; else if (reg == dloc->fbreg || is_fbreg) is_pointer = false; else if (arch__is(dloc->arch, "x86") && reg == X86_REG_SP) is_pointer = false; /* Get the type of the variable */ if (die_get_real_type(var_die, type_die) == NULL) { pr_debug_dtp("variable has no type\n"); ann_data_stat.no_typeinfo++; return -1; } /* * Usually it expects a pointer type for a memory access. * Convert to a real type it points to. But global variables * and local variables are accessed directly without a pointer. */ if (is_pointer) { if ((dwarf_tag(type_die) != DW_TAG_pointer_type && dwarf_tag(type_die) != DW_TAG_array_type) || die_get_real_type(type_die, type_die) == NULL) { pr_debug_dtp("no pointer or no type\n"); ann_data_stat.no_typeinfo++; return -1; } } /* Get the size of the actual type */ if (dwarf_aggregate_size(type_die, &size) < 0) { pr_debug_dtp("type size is unknown\n"); ann_data_stat.invalid_size++; return -1; } /* Minimal sanity check */ if ((unsigned)offset >= size) { pr_debug_dtp("offset: %d is bigger than size: %"PRIu64"\n", offset, size); ann_data_stat.bad_offset++; return -1; } return 0; } static struct type_state_stack *find_stack_state(struct type_state *state, int offset) { struct type_state_stack *stack; list_for_each_entry(stack, &state->stack_vars, list) { if (offset == stack->offset) return stack; if (stack->compound && stack->offset < offset && offset < stack->offset + stack->size) return stack; } return NULL; } static void set_stack_state(struct type_state_stack *stack, int offset, u8 kind, Dwarf_Die *type_die) { int tag; Dwarf_Word size; if (dwarf_aggregate_size(type_die, &size) < 0) size = 0; tag = dwarf_tag(type_die); stack->type = *type_die; stack->size = size; stack->offset = offset; stack->kind = kind; switch (tag) { case DW_TAG_structure_type: case DW_TAG_union_type: stack->compound = (kind != TSR_KIND_POINTER); break; default: stack->compound = false; break; } } static struct type_state_stack *findnew_stack_state(struct type_state *state, int offset, u8 kind, Dwarf_Die *type_die) { struct type_state_stack *stack = find_stack_state(state, offset); if (stack) { set_stack_state(stack, offset, kind, type_die); return stack; } stack = malloc(sizeof(*stack)); if (stack) { set_stack_state(stack, offset, kind, type_die);