2011-06-22 42 views
11

Tôi sẽ bắt đầu với câu hỏi cuối cùng: Trong C với gcc, có thể lấy (các) giá trị của __func__ (hoặc tương đương, __FUNCTION__) được lưu trữ trong một phần khác không phải là .rodata (hoặc bất cứ nơi nào -mrodata= điểm) hoặc tiểu mục đó?Buộc một số biến nhất định của trình biên dịch thành các phần ELF cụ thể (với gcc)

Lời giải thích đầy đủ:

Nói rằng tôi có một macro logging:

#define LOG(fmt, ...) log_internal(__FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__) 

(Chuỗi nối hành ## sử dụng trong bối cảnh unary tiêu thụ dấu phẩy trước khi và chỉ khi danh sách __VA_ARGS__ trống , do đó cho phép sử dụng chuỗi định dạng có hoặc không có đối số.)

Tôi có thể sử dụng macro bình thường:

void my_function(void) { 
    LOG("foo!"); 
    LOG("bar: %p", &bar); 
} 

có thể in (rõ ràng tùy thuộc vào việc thực hiện các log_internal):

foo.c:201(my_function) foo! 
foo.c:202(my_function) bar: 0x12345678 

Trong trường hợp này, chuỗi định dạng ("foo""bar: %p") và chuỗi preprocessor ("foo.c""my_function") là vô danh chỉ đọc dữ liệu và chúng được đặt tự động vào mục .rodata. Nhưng tôi muốn họ đi đến một nơi khác (tôi đang ở trên một nền tảng nhúng chạy gần như tất cả mọi thứ từ RAM cho tốc độ, nhưng hạn chế bộ nhớ đang đẩy để di chuyển một số thứ vào ROM). Đó là "dễ" để chuyển __FILE__ và chuỗi định dạng:

#define ROM_STR(str) (__extension__({static const __attribute__((__section__(".rom_data"))) char __c[] = (str); (const char *)&__c;})) 
#define LOG(fmt, ...) log_internal(ROM_STR(__FILE__), __LINE__, __func__, ROM_STR(fmt), ##__VA_ARGS__) 

Bạn không thể đặt một __attribute__ trên một chuỗi vô danh, vì vậy ROM_STR vĩ mô mang lại cho nó một cái tên thoáng qua, sẽ gắn nó vào một phần cụ thể, sau đó đánh giá lại đến địa chỉ bắt đầu, vì vậy nó có thể thay thế sạch sẽ. Điều này không hiệu quả nếu bạn cố chuyển một biến số char * thành LOG làm chuỗi định dạng của bạn, nhưng tôi sẵn sàng loại trừ trường hợp sử dụng đó.

Thông thường, các chuỗi vô danh xảy ra giống hệt nhau được trình biên dịch kết hợp thành một vị trí lưu trữ duy nhất, vì vậy mỗi phiên bản __FILE__ trong một tệp sẽ chia sẻ cùng một địa chỉ thời gian chạy. Với cách đặt tên rõ ràng trong ROM_STR, mỗi trường hợp sẽ nhận được vị trí lưu trữ riêng của mình, vì vậy có thể không thực sự hợp lý khi sử dụng nó trên __FILE__.

Tuy nhiên, tôi muốn sử dụng nó trên __func__. Vấn đề là __func__ không phải là cùng một loại ma thuật như __FILE__. Từ hướng dẫn gcc, "Chức năng Names như Strings":

Từ định danh __func__ được ngầm tuyên bố của người dịch như thể, ngay sau khi mở đôi của mỗi định nghĩa hàm, khai báo

static const char __func__[] = "function-name"; 

xuất hiện, trong đó hàm-tên là tên của hàm lexically-enclosing. Tên này là tên chưa được sắp xếp của hàm. ... Các số nhận dạng này không phải là các macro tiền xử lý. Trong GCC 3.3 và trước đó, và chỉ trong C, __FUNCTION____PRETTY_FUNCTION__ được coi là chuỗi ký tự; chúng có thể được sử dụng để khởi tạo các mảng char, và chúng có thể được nối với các chuỗi ký tự khác. GCC 3.4 và sau đó coi chúng là các biến, như __func__.

Vì vậy, nếu bạn quấn __func__ với ROM_STR, bạn sẽ có được

error: invalid initializer 

và nếu bạn cố gắng đặt một thuộc tính phần trước hoặc sau khi sử dụng __func__, bạn sẽ có được

error: expected expression before ‘__attribute__’ 

hoặc

error: expected ‘)’ before ‘__attribute__’ 

Và do đó chúng tôi lặp lại câu hỏi mở đầu: Có thể lấy __func__ được lưu trữ trong một phần lựa chọn của tôi không? Có lẽ tôi có thể sử dụng -fdata-sections và thực hiện một số ma thuật tập lệnh liên kết để nhận được .rodata.__func__.* bị loại trừ khỏi phần còn lại của .rodata? Nếu vậy, cú pháp cho globbing với loại trừ trong một kịch bản linker là gì? Nói cách khác, một nơi nào đó bạn có một *(.rodata*) - Tôi có thể đặt một ở một nơi khác, nhưng tôi sẽ cần phải sửa đổi glob ban đầu để loại trừ nó vì vậy tôi không nhận được hai bản sao.

Trả lời

2

Dường như tôi đã trả lời câu hỏi của riêng mình vào cuối với doanh nghiệp -fdata-sections, tôi chỉ không hiểu được GNU Linker đủ để xem nó. Tôi không thực sự cần globbing với loại trừ miễn là tôi xác định các bit đầu tiên. Bất kỳ phần nào kết quả phù hợp sẽ được đánh dấu là đã sử dụng, do đó, một phần sau cho số *(.rodata*) sẽ không tính hai lần và sao chép chúng ở một nơi khác. Tôi không cần phải gắn thẻ chúng với tất cả là ROM_STR. Mát mẻ!

Điều quan trọng cần lưu ý là -fdata-sections thực sự đặt từng chuỗi hàm vào phần .rodata.__func__.1234 riêng của mình (Tôi không chắc chắn về mẫu số theo sau). Tôi không biết liệu các chuỗi vô danh cũng có các phần riêng của họ hay không; nếu vậy, tôi có thể sử dụng cùng một thủ thuật trình liên kết để nắm bắt tất cả các chuỗi ẩn danh thay vì macro thuộc tính phần ROM_STR, nhưng nó có lẽ sẽ là một ý tưởng tồi. ROM_STR được sử dụng trong macro LOG, vì vậy, bảo đảm chỉ được áp dụng cho các chuỗi định dạng ghi nhật ký. Nếu tôi buộc tất cả các chuỗi vô danh vào ROM với một mẹo liên kết, điều đó sẽ bao gồm dữ liệu thông báo bình thường và tôi sẽ trả tiền phạt hiệu suất thời gian chạy để truy cập nó từ flash. Vì vậy, tôi không biết nếu nó thậm chí có thể, nhưng tư vấn của nó sẽ phụ thuộc vào yêu cầu hệ thống cụ thể của bạn.

+0

Dưới đây là cú pháp [tham chiếu đến cú pháp của trình liên kết hữu ích] (http://www.xgc.com/manuals/gcc-1750-ug/p5node10.html) –

+1

Tính đến năm 2015, gcc không còn thực hiện việc này nữa. Xem: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=192 –

3

Một cách tiếp cận, có thể là tin tặc hơn bạn muốn, sẽ là xen vào một kịch bản để thay đổi tên phần giữa quá trình biên soạn và lắp ráp. Ví dụ:

gcc -fdata-sections -S -o test.s test.c 
sed 's/^\t.section\t\.rodata\.__func__\.[0-9]*/\t.section .rom_data/' -i test.s 
gcc -c test.s 

Bạn cũng có thể thử viết một đường chuyền chuyển kêu vang để đặt __func__ tờ khai trong phần lựa chọn của bạn, hoặc viết một chương trình thao tác tập tin đối tượng sử dụng libbfd.

+0

Tôi nghĩ rằng quá trình hai bước để đổi tên các phần trong tệp lắp ráp trung gian chắc chắn có thể hoạt động. Với các tính năng bổ sung (không cần thiết), một tập lệnh có thể thậm chí chọn ra dữ liệu thích hợp nếu bạn xây dựng mà không có phần '-fdata'. Nhưng các thí nghiệm của tôi và đọc thêm về các kịch bản liên kết cho thấy rằng nó dễ dàng hơn nhiều so với tôi nghĩ để đặt những phần đó một cách rõ ràng, vì vậy tôi đã nêu chi tiết trong một câu trả lời khác. –

+0

Ngoài ra, +1 cho liên kết tới BFD. Điều đó có thể thuận tiện cho nhiều thứ. –

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