局部变量的地址给出了无效地址

问题描述 投票:0回答:1

我在代码中遇到了一个错误,其中本地堆栈变量的地址 是无效的。我一直在尝试使用 lldb 来调试它。

在下面的lldb提示符中,您可以看到

&dominance_frontier
给出了地址
0x0000000000000001
随后会在
hash_table_init
呼叫线路 121 上引发 SIGSEGV。 然而
&dominator_tree_adj
,给出了一个有效的地址。我完全困惑 为什么会出现这种情况。

Process 70998 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100006fd0 ir`ComputeDominanceFrontier(function=0x00006000030f8000) at dominators.c:121:9
   111          struct Array postorder_traversal = postorder (function->entry_basic_block);
   112          struct DFAConfiguration config = DominatorDFAConfiguration (function);
   113          struct DFAResult result = run_DFA (&config, function);
   114  
   115          HashTable dominator_tree_adj = ComputeDominatorTree (function, &result);
   116          printf("%p\n", &dominator_tree_adj);
   117          HashTable dominance_frontier;
   118          printf("%ld\n", sizeof(dominance_frontier));
   119  
   120  
-> 121          hash_table_init (&dominance_frontier);
   122          // Compute the transpose graph from the dominator tree adjacency list
   123          // Each node is guaranteed to have only one direct predecessor, since
   124          // each node can only have one immediate dominator. We will need this
   125          // in the DF algorithm below
   126          HashTable dominator_tree_transpose;
   127          hash_table_init (&dominator_tree_transpose);
   128  
   129          struct HashTableEntry *entry;
   130          size_t entry_iter = 0;
   131  
Target 0: (ir) stopped.
(lldb) p &dominance_frontier
(HashTable *) 0x0000000000000001
(lldb) p &dominator_tree_adj
(HashTable *) 0x000000016fdfef38

我一直在用

make DEBUG=yes

编译我的代码
OPT = -O3
FLAGS = -Wall -Wextra
CC = cc
OBJECTS =   ir_parser.o \
            main.o \
            threeaddr_parser.o \
            instruction.o \
            function.o \
            basicblock.o \
            constant.o \
            utils.o \
            value.o \
            array.o \
            mem.o \
            map.o \
            dfa.o \
            dominators.o


ifdef DEBUG
    OPT = -g
else
    OPT = -O3
endif


all: $(OBJECTS)
    $(CC) $(OPT) $(FLAGS) $^ -o ir

%.o: %.c *.h
    $(CC) $(OPT) $(FLAGS) -c $< -o $@


clean:
    rm *.o

这是段错误的位置:

Process 10619 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x9)
    frame #0: 0x0000000100005060 ir`_hash_table_init(table=0x0000000000000001, size=10) at map.c:21:21
   11   // Open addressing, linear probe hash table.
   12   
   13   unsigned long uint64_t_hash_function (uint64_t key)
   14   {
   15           // Simple hash function for demonstration
   16           return key;
   17   }
   18   
   19   void _hash_table_init (HashTable *table, size_t size)
   20   {
-> 21           table->size = size;
   22           table->count = 0;
   23           table->buckets = ir_calloc (table->size, sizeof (HashTableEntry));
   24   }
   25   void hash_table_init (HashTable *table)
   26   {
   27           _hash_table_init (table, MAP_INIT_SIZE_CNT);
   28   }
   29   
   30   // Create a new hash table
   31   HashTable *hash_table_create (size_t size)
Target 0: (ir) stopped.

输入文件:

fn test4(%1, %2) {

    alloca %9, 5
    add %3, %1, %2 
    store %9, %3
    cmp %4, %1, %2 
    jumpif 1, %4


    sub %5, %3, 1  
    jumpif 2, %5   

1:
    add %6, %1, 20 
    jump 3    


2:
    add %7, %2, 30 

    
3:
    sub %8, %3, %3 
}
./ir -f path/to/input/file

我正在使用 Apple clang 版本 15.0.0 (clang-1500.3.9.4)

完整代码在这里:https://github.com/CoconutJJ/compiler-optimization/blob/d3244e8c9f96e8180c924533abf8f5daac15238c/ir/dominators.c#L115

c lldb
1个回答
0
投票

统治者.c

HashTable ComputeDominanceFrontier (struct Function *function)
^^^^^^^^^

统治者.h

void ComputeDominanceFrontier (struct Function *function);
^^^^

哎呀。

函数

ComputeDominanceFrontier
是从
main.c
调用的,其中包含
dominators.h
,因此调用者认为它返回
void
。 但根据
dominators.c
中的定义,它实际上返回的是
HashTable
。 这种不匹配是导致崩溃的原因,如下所述。

编译器没有捕获到这一点,因为您没有将

dominators.h
包含到
dominators.c
中,因此在编译
dominators.c
时,它不知道其他一些源文件看到了冲突的声明。 因此,当标头声明全局函数时,应始终确保标头包含在定义该函数的源文件中。 您可以使用
-Wmissing-prototypes
来帮助强制执行此操作,只要在未事先声明的情况下定义全局函数,它就会发出警告。 请参阅针对范围内没有原型定义的函数的编译器警告?


发生的情况是这样的:在arm64上,像许多其他平台一样,当函数返回无法在寄存器中返回的

struct
类型时,它会通过“隐藏引用”返回:调用者为返回值分配空间,并传递一个额外的隐藏参数和该空间的地址。 在arm64上,额外的参数在寄存器
x8
中传递。 然后被调用的函数负责将其返回值复制到该空间中。

在这种情况下,由于

main
不知道
ComputeDominanceFrontier
正在返回
struct
类型,因此它没有分配这样的空间,并留下
x8
包含垃圾(在你的情况下,它的值恰好是
1
)。

如果天真地编译,当

dominance_frontier
从堆栈复制到伪造的返回值地址时,您会期望在函数末尾看到崩溃。 听起来这就是您在 Ubuntu 测试中看到的情况(让我猜一下,使用 gcc 而不是 clang?)。

但是,编译器可以对此进行优化:不使用

dominance_frontier
的堆栈空间,而是使用调用者分配的返回值空间。 然后我们不需要在返回之前复制该数据,因为它是“就地”填充的。 看来,即使优化“关闭”,clang 也会执行此优化。 因此,
dominance_frontier
并不真正存在于堆栈中,并且
&dominance_frontier
不是堆栈地址,而是调用者传递的返回值地址 - 在这种情况下是垃圾。

© www.soinside.com 2019 - 2024. All rights reserved.