2015-06-08 13 views
8

Tôi đang cố gắng xử lý đầu ra của một nm hoặc readelf -s trên một tệp thực thi. Tuy nhiên, tôi gặp khó khăn trong việc phân biệt các hàm tĩnh với nhau trong đầu ra.Làm thế nào tôi có thể phân biệt các chức năng tĩnh với đầu ra nm hoặc đọc trong C

Dưới đây là những gì tôi đang làm việc với:

test.c

static int foo() { 
    int x = 6; 
} 

main() {} 

other.c

static int foo() { 
    int x = 5; 
} 

tôi biên soạn những như vậy:

gcc -o test test.c other.c 

Và sau đó chạy một lệnh nm để có được tất cả các biểu tượng:

nm test 

Trong đó hai biểu tượng sau (đối với chức năng tĩnh của tôi) xuất hiện:

00000000004004ed t foo 
0000000000400500 t foo 

Có một phương pháp để có thể thế nào để phân biệt các tập tin chức năng foo cụ thể xuất hiện từ đâu? Hoặc tôi sẽ cần phải làm một số phép thuật trước khi biên dịch để có được điều này để làm việc?

Tôi nên thêm điều đó vào trường hợp sử dụng của mình, tôi có quyền truy cập vào tệp nhị phân cuối cùng và tệp đối tượng được sử dụng, nhưng tôi không thể tự xây dựng nó để đảm bảo rằng nó có bảng biểu tượng.

Cảm ơn!

+1

theo như tôi biết thông tin tệp nguồn được lưu trữ trong thông tin gỡ lỗi. Hoàn toàn bị tước nhị phân sẽ không có thông tin như vậy. Để đọc thông tin gỡ lỗi, hãy xem tùy chọn 'readelf' --debug-dump. Tương tự --debug-syms cho 'nm' –

+0

Biên dịch với' -g' và sử dụng 'nm -l' – 4566976

+0

Tôi nên thêm vào trường hợp sử dụng của mình, tôi có quyền truy cập vào tệp nhị phân cuối cùng và các tệp đối tượng được sử dụng bởi nó, nhưng Tôi không thể xây dựng hoặc đảm bảo thông tin gỡ lỗi trong nhị phân. – Andrew

Trả lời

9

Câu hỏi của bạn giả định rằng, đưa ra một thực thi, bạn luôn có thể khám phá tên của các static chức năng (địa phương) đã được biên soạn vào nó, sử dụng nm hoặc công cụ khác. Vì vậy, bạn sẽ có thể thấy khi nào hai hoặc nhiều tên như vậy giống nhau và nêu lên câu hỏi về cách khám phá những tệp nguồn nào chúng được biên soạn.

Tuy nhiên, giả định đó là sai. Trong trường hợp của gcc, nếu tệp được biên dịch với tối ưu hóa -O0 thì biểu tượng cục bộ sẽ được phát ra trong đối tượng biểu tượng tệp bảng. -O0 là mặc định, vì vậy nó được áp dụng trong trường hợp của bạn:

gcc -o test test.c other.c 

Nhưng nếu file được biên dịch tại bất kỳ mức độ tối ưu hóa cao hơn - như họ chắc chắn sẽ cho phát hành xây dựng - sau đó các ký hiệu địa phương bị bỏ qua từ đối tượng tệp biểu tượng bảng. Vì vậy, các mối liên kết thậm chí không bao giờ nhìn thấy chúng. Vì vậy, bạn không thể khôi phục chúng từ tệp thực thi với nm hoặc bất kỳ thứ gì khác.

Biên dịch file mẫu của bạn với:

gcc -O1 -o test test.c other.c 

sau đó nm test một lần nữa, và bạn sẽ nhận thấy rằng:

00000000004004ed t foo 
0000000000400500 t foo 

đã biến mất, cùng với tất cả các tên hàm tĩnh khác. Trong trường hợp đó, nếu như bạn nói bạn không thể kiểm soát cách thực thi được xây dựng, thì bạn không thể đảm bảo rằng đó là thậm chí có thể cho câu hỏi của bạn phát sinh.

Nếu bạn thể kiểm soát như thế nào thực thi được xây dựng để đảm bảo rằng các tập tin được biên soạn với -O0, sau đó có một số cách mà bạn có thể buộc các tên hàm tĩnh để tập tin nguồn.Hai người đều đơn giản sẽ là:

readelf -s test 

objdump -t test 

mỗi trong số đó sẽ liệt kê một tên tập tin nguồn ở phần đầu của mỗi đoạn biểu tượng đến từ nó.

(Và nếu nó cần nói, cách tiếp cận gdb đề xuất bởi bởi @Amol không thoát khỏi những hạn chế đó thực thi phải được biên soạn với tối ưu hóa -O0)

2

Bạn có thể cần phải đọc bảng biểu tượng ELF và trích xuất giá trị ELF32_ST_BIND.

Theo đặc tả ELF (xem http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf) các giá trị cho ELF32_ST_BIND có thể là:.

 Name   Value 
    STB_LOCAL  0 
    STB_GLOBAL  1 
    STB_WEAK  2 
    STB_LOPROC 13 
    STB_HIPROC 15 

đâu STB_LOCAL được định nghĩa là "biểu tượng Local không nhìn thấy được bên ngoài đối tượng tập tin chứa định nghĩa của họ những biểu tượng Local của cùng một tên có thể tồn tại trong nhiều tệp mà không can thiệp lẫn nhau. " có vẻ phù hợp khá tốt với các hàm tĩnh C.

Ví dụ, đưa bạn nếm thử và sửa đổi nó một chút:

test.c: 

    static int foo() { 
     int x = 5; 
    } 

    int bar() 
    { 
     int y = 6; 
    } 

    main() {} 

    other.c: 

    static int foo() 
    { 
     int x = 7; 
    } 

và biên soạn với gcc -o test test.c other.c và nhìn vào bảng biểu tượng (rất nhiều entry loại bỏ):

readelf -s test 
    Num: Value   Size Type Bind Vis  Ndx Name 
    37: 00000000004004f0 13 FUNC LOCAL DEFAULT 13 foo 
    39: 0000000000400510 13 FUNC LOCAL DEFAULT 13 foo 
    52: 00000000004004fd 13 FUNC GLOBAL DEFAULT 13 bar 

Chúng ta có thể thấy rằng hai hàm tĩnh hiển thị dưới dạng LOCAL và hàm 'bình thường' hiển thị GLOBAL

Lưu ý: trong khi phương pháp này sẽ hoạt động với các tệp không gỡ lỗi, nếu thứ e tập tin cuối cùng bị tước, chúng tôi không thể sử dụng phương pháp này.

3

Tôi đã thử theo trình tự sau.

Nếu bạn có tệp đầu ra bị tước mà không có biểu tượng gỡ lỗi thì sử dụng gdb bạn có thể tạo tệp đối tượng. Thực hiện theo các lệnh như sau:

$ gdb a.out 

sẽ cung cấp sau khi sản lượng

Reading symbols from /home/amol/amol/a.out...(no debugging symbols found)...done. 

Sau đó (gdb) sẽ đến trong thiết bị đầu cuối

cho lệnh theo thứ tự sau đây ((gdb) nhắc đến theo mặc định khi bạn tiếp tục nhập lệnh)

(gdb) maint print symbols filename 
(gdb) maint print psymbols filename 
(gdb) maint print msymbols filename 

Bây giờ trong thư mục của bạn, bạn có thể thấy một tệp có tên là tên tệp. Mở tập tin này trong trình soạn thảo văn bản thì bạn có thể xem thông tin như sau:

[ 8] t 0x80483b4 foo section .text test.c 
[ 9] T 0x80483c3 main section .text other.c 
[10] t 0x80483c8 foo section .text other.c 

Ở đây bạn có thể thấy rõ rằng đó foo() chức năng là đến từ đó .c tập tin. Hy vọng điều này sẽ giúp ích cho bạn.

+0

Cảm ơn bạn đã trả lời. Có lẽ tôi đã đề cập rằng tôi muốn phương pháp của tôi để thực hiện bất kỳ thực thi tùy ý, do đó, một bảng biểu tượng thậm chí có thể được bỏ qua và phương pháp vẫn nên hoạt động. Tuy nhiên khi xem câu trả lời của bạn và một số người khác tôi không nghĩ rằng điều này là có thể. Cảm ơn một lần nữa! – Andrew

0

Nếu nó phải làm việc trên bất kỳ thực thi tước bạn có thể cấy ghép một chuỗi thành các hàm và tìm kiếm chúng trong tệp thực thi.

#define STR1(x) #x 
#define STR(x) STR1(x) 
#define FUNCID(funcname) __asm__ __volatile__ (\ 
    "jmp 1f;"\ 
    ".string \"" __FILE__ "/" STR(funcname) "()\";"\ 
    "1:"\ 
) 

static int foo() { 
    FUNCID(foo); 
    return rand(); 
} 
Các vấn đề liên quan