2013-05-23 29 views
11

Biến cục bộ tĩnh được lưu trữ trong bộ nhớ ở đâu? Các biến cục bộ chỉ có thể được truy cập bên trong hàm mà chúng được khai báo.Biến cục bộ tĩnh ở đâu

Biến tĩnh toàn cầu đi vào phân đoạn .data.

Nếu cả tên của biến cục bộ tĩnh và tĩnh cục bộ đều giống nhau, trình biên dịch phân biệt chúng như thế nào?

+2

Tại sao bạn nghĩ rằng tên trình biên dịch sử dụng ở cấp liên kết là cùng tên mà bạn khai báo? – bmargulies

+0

thể trùng lặp của một này: [gì “tĩnh” có nghĩa là trong một chương trình C] [1] [1]: http: // stackoverflow.com/questions/572547/what-does-static-mean-in-ac-program – tejas

+1

có thể trùng lặp của [Các biến tĩnh được lưu trữ ở đâu (trong C/C++)?] (http://stackoverflow.com/questions/93039/ nơi-là-tĩnh-biến-được lưu trữ-in-cc) – jogojapan

Trả lời

11

Biến tĩnh đi vào cùng một phân đoạn dưới dạng biến toàn cục. Điều duy nhất khác nhau giữa hai là trình biên dịch "ẩn" tất cả các biến tĩnh từ trình liên kết: chỉ các tên của các biến extern (global) được tiếp xúc. Đó là cách trình biên dịch cho phép các biến tĩnh có cùng tên tồn tại trong các đơn vị dịch thuật khác nhau. Tên của các biến tĩnh vẫn được biết đến trong giai đoạn biên dịch, nhưng sau đó dữ liệu của chúng được đặt vào phân đoạn .data nặc danh.

2

Biến tĩnh gần như tương tự như biến toàn cầu và do đó biến tĩnh chưa được khởi tạo nằm trong BSS và biến tĩnh được khởi tạo nằm trong phân đoạn dữ liệu.

0

Như đã đề cập bởi dasblinken, GCC 4.8 đặt các thống kê địa phương trên cùng một vị trí như hình cầu.

Chính xác hơn:

  • static int i = 0 đi trên .bss
  • static int i = 1 đi trên .data

Hãy phân tích một Linux x86-64 dụ ELF nhìn thấy nó mình:

#include <stdio.h> 

int f() { 
    static int i = 1; 
    i++; 
    return i; 
} 

int main() { 
    printf("%d\n", f()); 
    printf("%d\n", f()); 
    return 0; 
} 

Để đạt được kết luận, chúng ta cần hiểu thông tin di dời. Nếu bạn chưa bao giờ chạm vào điều đó, hãy xem xét reading this post first.

Biên dịch nó:

gcc -ggdb -c main.c 

Decompile mã với:

objdump -S main.o 

f chứa:

int f() { 
    0: 55      push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    static int i = 1; 
    i++; 
    4: 8b 05 00 00 00 00  mov 0x0(%rip),%eax  # a <f+0xa> 
    a: 83 c0 01    add $0x1,%eax 
    d: 89 05 00 00 00 00  mov %eax,0x0(%rip)  # 13 <f+0x13> 
    return i; 
    13: 8b 05 00 00 00 00  mov 0x0(%rip),%eax  # 19 <f+0x19> 
} 
    19: 5d      pop %rbp 
    1a: c3      retq 

nào làm 3 truy cập để i:

  • 4 di chuyển đến eax để chuẩn bị cho increment
  • d di chuyển giá trị tăng lên trở lại vào bộ nhớ
  • 13 di chuyển i đến eax cho giá trị trả về. Rõ ràng là không cần thiết vì eax đã chứa nó và -O3 có thể xóa điều đó.

Vì vậy, hãy chỉ tập trung vào 4:

4: 8b 05 00 00 00 00  mov 0x0(%rip),%eax  # a <f+0xa> 

Hãy nhìn vào các dữ liệu di dời:

readelf -r main.o 

mà nói cách các địa chỉ phần văn bản sẽ được điều chỉnh bởi các mối liên kết khi nó là làm cho thực thi.

Nó chứa:

Relocation section '.rela.text' at offset 0x660 contains 9 entries: 
    Offset   Info   Type   Sym. Value Sym. Name + Addend 
000000000006 000300000002 R_X86_64_PC32  0000000000000000 .data - 4 

Chúng tôi nhìn vào .rela.text và không phải là những người khác bởi vì chúng tôi quan tâm đến việc di dời của .text.

Offset 6 rơi ngay vào hướng dẫn mà bắt đầu từ byte 4:

4: 8b 05 00 00 00 00  mov 0x0(%rip),%eax  # a <f+0xa> 
      ^^ 
      This is offset 6 

Từ kiến ​​thức của chúng ta về mã hóa lệnh x86-64:

  • 8b 05 là phần mov
  • 00 00 00 00 là phần địa chỉ, bắt đầu từ byte 6

AMD64 System V ABI Update cho chúng ta biết R_X86_64_PC32 hành vi trên 4 byte (00 00 00 00) và tính toán các địa chỉ như:

S + A - P 

có nghĩa là:

  • S: phân khúc chỉ vào: .data
  • A: số Added: -4
  • P: địa chỉ của byte 6 khi nạp

-P là cần thiết vì GCC sử dụng RIP thân giải quyết, vì vậy chúng tôi phải giảm giá vị trí trong .text

-4 là cần thiết vì RIP điểm để hướng dẫn sau tại byte 0xA nhưng P là byte 0x6, vì vậy chúng tôi cần giảm giá 4.

Kết luận: sau khi liên kết nó sẽ trỏ đến byte đầu tiên của đoạn .data.

Các vấn đề liên quan