2012-02-26 37 views
10

Sự cốLàm thế nào để 'liên kết' tập tin đối tượng để thực thi/biên dịch nhị phân?

Tôi muốn đưa tệp đối tượng vào nhị phân hiện có. Như một ví dụ cụ thể, hãy xem xét một nguồn Hello.c:

#include <stdlib.h> 

int main(void) 
{ 
    return EXIT_SUCCESS; 
} 

Nó có thể được biên dịch để thực thi một cách đặt tên Hello qua gcc -std=gnu99 -Wall Hello.c -o Hello. Hơn nữa, bây giờ xem xét Embed.c:

func1(void) 
{ 
} 

Một đối tượng tập tin Embed.o thể được tạo ra từ này thông qua gcc -c Embed.c. Câu hỏi của tôi là cách chèn thông thường Embed.o vào Hello sao cho các chuyển vị cần thiết được thực hiện và các bảng nội bộ ELF thích hợp (ví dụ: bảng biểu tượng, PLT, v.v.) được vá đúng cách?


Giả

Nó có thể được giả định rằng các tập tin đối tượng được nhúng có phụ thuộc của nó liên kết tĩnh rồi. Bất kỳ phụ thuộc động nào, chẳng hạn như thời gian chạy C có thể được giả định là có mặt trong thực thi đích.


Những nỗ lực hiện tại/Ý tưởng

  • Sử dụng libbfd để sao chép phần từ tập tin đối tượng vào nhị phân. Tiến trình tôi đã thực hiện với điều này là tôi có thể tạo một đối tượng mới với các phần từ nhị phân gốc và các phần từ tệp đối tượng. Vấn đề là do tệp đối tượng có thể định vị lại, nên các phần của nó không thể được sao chép đúng vào đầu ra mà không thực hiện các chuyển vị trí đầu tiên.
  • Chuyển đổi tệp nhị phân trở lại thành tệp đối tượng và liên kết lại với ld. Cho đến nay tôi đã thử sử dụng objcopy để thực hiện chuyển đổi objcopy --input elf64-x86-64 --output elf64-x86-64 Hello Hello.o. Rõ ràng điều này không hoạt động như tôi dự định kể từ khi ld -o Hello2 Embed.o Hello.o sau đó sẽ dẫn đến ld: error: Hello.o: unsupported ELF file type 2. Tôi đoán điều này nên được mong đợi mặc dù kể từ khi Hello không phải là một tập tin đối tượng.
  • Tìm công cụ hiện có thực hiện loại chèn này?

(Không bắt buộc đọc)

Tôi đang làm cho một trình soạn thảo thực thi tĩnh, nơi mà tầm nhìn là để cho phép các thiết bị đo đạc của thói quen người dùng định nghĩa tùy ý vào một nhị phân hiện có. Thao tác này sẽ hoạt động theo hai bước:

  1. Việc tiêm tệp đối tượng (chứa thói quen do người dùng xác định) vào nhị phân. Đây là bước bắt buộc và không thể làm việc xung quanh bằng các lựa chọn thay thế chẳng hạn như tiêm đối tượng dùng chung thay thế.
  2. Thực hiện phân tích tĩnh trên nhị phân mới và sử dụng phép phân tích tĩnh này từ các mã gốc sang mã mới được thêm vào.

Tôi có, phần lớn, đã hoàn thành công việc cần thiết cho bước 2, nhưng tôi gặp sự cố với việc tiêm tệp đối tượng. Vấn đề chắc chắn là có thể giải quyết được vì các công cụ khác sử dụng cùng một phương pháp tiêm đối tượng (ví dụ: EEL).

+0

Một đọc nhanh các câu hỏi để lại cảm giác rằng khái niệm giữa một trình liên kết thời gian chạy và một trình liên kết thông thường không được hiểu. Trình liên kết thời gian chạy/trình liên kết/chương trình chỉ hoạt động trên các định dạng dễ sửa và nhanh chóng. .o không phải là một trong những :-) Nếu nó có phụ thuộc tối thiểu, như một codec, liên kết với mã tối thiểu để làm cho nó một .so âm thanh như các tuyến đường logic –

+0

@MarcovandeVoort: Cảm ơn bạn đã bình luận :) Tôi đã sử dụng 'liên kết 'thuật ngữ lỏng lẻo, như người ta có thể sử dụng' tiêm ', đó là lý do tại sao tôi đặt nó trong dấu ngoặc kép. Một trong những lý do tôi không thể biến nó thành '.so', là các thủ thuật tiêm như' LD_PRELOAD' có thể bị ứng dụng phá hoại. Không chỉ vậy, nó đòi hỏi sự phân bố của một thư viện bổ sung tạo thành môi trường mới. Việc tẩy lông tĩnh có nhiều ưu điểm khác nhau (đặc biệt là cho mục đích của dự án này), nhưng như tôi đã nói cả trong câu hỏi và nhận xét cho câu trả lời, đây không phải là quyết định thiết kế mà tôi có thể thay đổi :) –

+0

Bạn đang cố gắng làm điều gì đó như khả năng của ld trên AIX (và không nơi nào khác mà tôi biết) để liên kết lại một tệp thực thi mà chỉ có một tệp đối tượng đã thay đổi? –

Trả lời

0

Bạn không thể thực hiện việc này theo bất kỳ cách thực tế nào. Các giải pháp dự định là làm cho đối tượng đó thành một lib chia sẻ và sau đó gọi dlopen trên đó.

+0

Cảm ơn câu trả lời của bạn. Xin vui lòng xem ý kiến ​​của tôi để Dan Fego. Cụ thể đây là một yêu cầu tôi không thể thay đổi. Tôi không chắc chắn rằng nó không thể được thực hiện 'một cách thực tế' kể từ khi công cụ EEL hiện tại thực hiện điều này. –

+0

Tôi không biết điều gì đã gây khó hiểu cho các yêu cầu của bạn, nhưng nhấn mạnh rằng ao có thể kéo được thay vì một .so chứa nó đáp ứng định nghĩa của tôi về 'lunatic'. Định nghĩa của tôi về 'thực tế' là 'với một mức độ nỗ lực từ xa thích hợp. Nếu quản lý của bạn muốn bạn dành nhiều thời gian để đạt được điều này, bạn có sự thông cảm của tôi. – bmargulies

+0

Điều này là dành cho nghiên cứu của tôi dựa trên luận án thạc sĩ ... –

4

Nếu nó đã được tôi, tôi muốn nhìn để tạo Embed.c vào một đối tượng chia sẻ, libembed.so, như vậy:

gcc -Wall -shared -fPIC -o libembed.so Embed.c 

Điều đó sẽ tạo ra một đối tượng chia sẻ relocatable từ Embed.c. Cùng với đó, bạn có thể buộc nhị phân mục tiêu của bạn để tải đối tượng chia sẻ điều này bằng cách thiết lập các biến môi trường LD_PRELOAD khi chạy nó (xem thêm thông tin here):

LD_PRELOAD=/path/to/libembed.so Hello 

Các "lừa" ở đây sẽ tìm ra cách để làm thiết bị của bạn, đặc biệt là xem xét nó là một thực thi tĩnh. Ở đó, tôi không thể giúp bạn, nhưng đây là một cách để có mã hiện diện trong một không gian bộ nhớ. Có thể bạn sẽ muốn làm một số loại khởi tạo trong một constructor, mà bạn có thể làm với một thuộc tính (nếu bạn đang sử dụng gcc, ít nhất):

void __attribute__ ((constructor)) my_init() 
{ 
    // put code here! 
} 
+0

Có, đây là giải pháp thay thế cho việc thực hiện tẩy rửa. Liên quan đến vấn đề làm thế nào để thực hiện việc vá lỗi, nó có thể được thực hiện với thuộc tính GCC __attribute __ ((constructor)) cho phép một phương thức được gọi khi thư viện được nạp. Việc thực thi cũng có thể bị lừa khi nghĩ rằng đối tượng được chia sẻ là một phụ thuộc. Đây là phương pháp được sử dụng bởi một công cụ hiện có được gọi là LEEL. –

+0

Thật không may, chạy vòng/chạy động sẽ không phải là giải pháp có thể chấp nhận được. Đây là một yêu cầu được khai báo rõ ràng tại thời điểm bắt đầu dự án. –

0

Vấn đề là của .o không đầy đủ liên kết được nêu ra, và hầu hết các tài liệu tham khảo vẫn còn mang tính biểu tượng. Các tệp nhị phân (thư viện được chia sẻ và tệp thực thi) là một bước gần hơn với mã được liên kết cuối cùng.

Thực hiện bước liên kết đến một lib được chia sẻ, không có nghĩa là bạn phải tải nó qua trình tải lib động. Đề xuất này nhiều hơn là trình tải riêng cho một tệp nhị phân hoặc lib được chia sẻ có thể đơn giản hơn cho .o.

Một khả năng khác là tùy chỉnh quy trình liên kết đó và gọi cho trình liên kết và liên kết nó để được tải trên một số địa chỉ cố định. Bạn cũng có thể xem xét việc chuẩn bị ví dụ: bộ nạp khởi động, cũng liên quan đến bước liên kết cơ bản để thực hiện chính xác điều này (sửa một đoạn mã thành địa chỉ tải đã biết).

Nếu bạn không liên kết đến một địa chỉ cố định và muốn di chuyển thời gian chạy, bạn sẽ phải viết một trình liên kết cơ bản lấy tệp đối tượng, chuyển nó đến địa chỉ đích bằng cách thực hiện các sửa lỗi thích hợp.

Tôi cho rằng bạn đã có nó, thấy đó là luận văn chính của bạn, nhưng cuốn sách này: http://www.iecc.com/linker/ là phần giới thiệu tiêu chuẩn về điều này.

+0

Tôi thực sự cũng đã cân nhắc việc tùy chỉnh quy trình liên kết, đó là những gì tôi đã hỏi trong câu hỏi ở đây: http://stackoverflow.com/questions/9508290/how-to-specify-base-addresses-for-sections-when-linking - hay - cách khác. Nếu tôi có thể liên kết các phần tại một địa chỉ nhất định, tôi nghĩ rằng tôi có thể sao chép chúng vào tệp thực thi bằng cách sử dụng 'libbfd'. Bạn có biết về một công cụ hoặc tùy chọn liên kết mà sẽ cho phép những gì bạn đang đề xuất (liên kết các phần - không phải biểu tượng - đến địa chỉ cố định)? –

+0

Như đã nói trong câu hỏi khác: các tệp tài nguyên liên kết là cách để đi. –

0

Bạn đã xem số DyninstAPI chưa? Nó xuất hiện hỗ trợ gần đây đã được thêm vào để liên kết một .o vào một thực thi tĩnh.

Từ các trang web phát hành:

hỗ trợ Ổ ghi nhị phân cho những chương trình liên kết tĩnh trên nền tảng x86 và x86_64

+0

Cảm ơn bạn đã liên kết này. Tôi đã thấy «Dyninst' trước đây nhưng không biết nó cũng đã viết lại nhị phân tĩnh. Tôi sẽ xem xét điều này và cập nhật sau. –

0

Bạn phải nhường chỗ cho các mã định vị để phù hợp với thực thi bằng cách mở rộng thực thi phân đoạn văn bản, giống như nhiễm vi-rút. Sau đó, sau khi viết mã relocatable vào không gian đó, hãy cập nhật bảng biểu tượng bằng cách thêm các biểu tượng cho bất kỳ thứ gì trong đối tượng có thể định vị lại đó, và sau đó áp dụng các tính toán di chuyển cần thiết.Tôi đã viết mã thực hiện điều này khá tốt với 32bit của ELF.

+0

Chào mừng bạn đến với Stack Overflow. Vui lòng giải thích một số mã này bạn đã viết để giải quyết vấn đề này - tất cả đều nói cho chúng tôi biết bạn có nó, nhưng nó không giúp được gì ngay bây giờ. – michaelb958

0

Chuỗi thú vị. Tôi có một ví dụ cụ thể khác về lý do tại sao điều này có ý nghĩa.

Tôi đang chơi với xây dựng một công cụ mã hóa thời gian chạy nhị phân sẽ hoạt động trên các chương trình đã được biên dịch. Những gì tôi muốn làm điều này là:

1) Mã hóa phần nhất định của một gia tinh (.text và như vậy)

2) liên kết lại elf với thói quen giải mã của tôi và một hàm __attribute__((constructor)) mà các cuộc gọi giải mã trên các phần được mã hóa

Bằng cách đó, thao tác này sẽ hoạt động với bất kỳ chương trình nào mà họ không biết.

Tôi đã không tìm thấy cách dễ dàng để thực hiện việc này, vì vậy tôi có thể phải tách riêng mình ra và tự mình thêm nội dung vào đó.

0

Giả sử mã nguồn cho tệp thực thi đầu tiên có sẵn và được biên dịch bằng tập lệnh liên kết phân bổ không gian cho (các) tệp đối tượng sau này, có một giải pháp tương đối đơn giản hơn. Vì tôi hiện đang làm việc trên một ví dụ về dự án ARM dưới đây được biên dịch với trình biên dịch chéo GNU ARM.

Primary tập tin mã nguồn, hello.c

#include <stdio.h> 

int main() 
{ 

    return 0; 
} 

được xây dựng với một kịch bản mối liên kết đơn giản phân bổ không gian cho một đối tượng được nhúng sau:

SECTIONS 
{ 
    .text : 
    { 
     KEEP (*(embed)) ; 

     *(.text .text*) ; 
    } 
} 

Giống như:

arm-none-eabi-gcc -nostartfiles -Ttest.ld -o hello hello.c 
readelf -s hello 

Num: Value Size Type Bind Vis  Ndx Name 
0: 00000000  0 NOTYPE LOCAL DEFAULT UND 
1: 00000000  0 SECTION LOCAL DEFAULT 1 
2: 00000000  0 SECTION LOCAL DEFAULT 2 
3: 00000000  0 SECTION LOCAL DEFAULT 3 
4: 00000000  0 FILE LOCAL DEFAULT ABS hello.c 
5: 00000000  0 NOTYPE LOCAL DEFAULT 1 $a 
6: 00000000  0 FILE LOCAL DEFAULT ABS 
7: 00000000 28 FUNC GLOBAL DEFAULT 1 main 

Bây giờ, hãy cho phép biên dịch đối tượng được nhúng có nguồn nằm trong embed.c

void func1() 
{ 
    /* Something useful here */ 
} 

biên dịch lại với cùng một kịch bản mối liên kết lần này chèn các biểu tượng mới:

arm-none-eabi-gcc -c embed.c 
arm-none-eabi-gcc -nostartfiles -Ttest.ld -o new_hello hello embed.o 

Xem kết quả:

readelf -s new_hello 
Num: Value Size Type Bind Vis  Ndx Name 
0: 00000000  0 NOTYPE LOCAL DEFAULT UND 
1: 00000000  0 SECTION LOCAL DEFAULT 1 
2: 00000000  0 SECTION LOCAL DEFAULT 2 
3: 00000000  0 SECTION LOCAL DEFAULT 3 
4: 00000000  0 FILE LOCAL DEFAULT ABS hello.c 
5: 00000000  0 NOTYPE LOCAL DEFAULT 1 $a 
6: 00000000  0 FILE LOCAL DEFAULT ABS 
7: 00000000  0 FILE LOCAL DEFAULT ABS embed.c 
8: 0000001c  0 NOTYPE LOCAL DEFAULT 1 $a 
9: 00000000  0 FILE LOCAL DEFAULT ABS 
10: 0000001c 20 FUNC GLOBAL DEFAULT 1 func1 
11: 00000000 28 FUNC GLOBAL DEFAULT 1 main 
Các vấn đề liên quan