2012-03-15 2 views
8

Câu hỏi này theo sau từ another question Tôi đã hỏi trước đây. Tóm lại, đây là một trong những nỗ lực của tôi khi hợp nhất hai tệp thực thi được liên kết đầy đủ vào một tệp thực thi được liên kết đầy đủ. Sự khác biệt là câu hỏi trước đó đề cập đến việc sáp nhập một tệp đối tượng vào một tệp thực thi được liên kết đầy đủ, điều này thậm chí còn khó hơn bởi vì nó có nghĩa là tôi cần phải xử lý thủ công các chuyển vị.Làm cách nào để hợp nhất hai tệp thực thi nhị phân?

Những gì tôi đã được các tập tin sau đây:

example-target.c:

#include <stdlib.h> 
#include <stdio.h> 

int main(void) 
{ 
    puts("1234"); 
    return EXIT_SUCCESS; 
} 

example-embed.c:

#include <stdlib.h> 
#include <stdio.h> 

/* 
* Fake main. Never used, just there so we can perform a full link. 
*/ 
int main(void) 
{ 
    return EXIT_SUCCESS; 
} 

void func1(void) 
{ 
    puts("asdf"); 
} 

Mục tiêu của tôi là để hợp nhất hai tập tin thực thi những để tạo ra một thực thi chính thức là giống như example-target, nhưng cũng có thêm mainfunc1.

Từ quan điểm của thư viện BFD, mỗi nhị phân được tạo thành (trong số những thứ khác) của một tập hợp các phần. Một trong những vấn đề đầu tiên tôi gặp phải là những phần này có địa chỉ tải xung đột (như vậy nếu tôi hợp nhất chúng, các phần sẽ chồng lên nhau).

Điều tôi đã làm để giải quyết vấn đề này là phân tích example-target theo chương trình để có được danh sách địa chỉ tải và kích cỡ của từng phần của nó. Sau đó tôi đã làm tương tự cho example-embed và sử dụng thông tin này để tự động tạo một linker command cho example-embed.c để đảm bảo rằng tất cả các phần của nó được liên kết tại các địa chỉ không trùng lặp với bất kỳ phần nào trong example-target. Do đó example-embed thực sự được liên kết đầy đủ hai lần trong quá trình này: một lần để xác định có bao nhiêu phần và kích thước của chúng là gì và một lần nữa liên kết với một đảm bảo rằng không có xung đột phần nào với example-target.

Trên hệ thống của tôi, lệnh mối liên kết sản xuất là:

-Wl,--section-start=.new.interp=0x1004238,--section-start=.new.note.ABI-tag=0x1004254, 
--section-start=.new.note.gnu.build-id=0x1004274,--section-start=.new.gnu.hash=0x1004298, 
--section-start=.new.dynsym=0x10042B8,--section-start=.new.dynstr=0x1004318, 
--section-start=.new.gnu.version=0x1004356,--section-start=.new.gnu.version_r=0x1004360, 
--section-start=.new.rela.dyn=0x1004380,--section-start=.new.rela.plt=0x1004398, 
--section-start=.new.init=0x10043C8,--section-start=.new.plt=0x10043E0, 
--section-start=.new.text=0x1004410,--section-start=.new.fini=0x10045E8, 
--section-start=.new.rodata=0x10045F8,--section-start=.new.eh_frame_hdr=0x1004604, 
--section-start=.new.eh_frame=0x1004638,--section-start=.new.ctors=0x1204E28, 
--section-start=.new.dtors=0x1204E38,--section-start=.new.jcr=0x1204E48, 
--section-start=.new.dynamic=0x1204E50,--section-start=.new.got=0x1204FE0, 
--section-start=.new.got.plt=0x1204FE8,--section-start=.new.data=0x1205010, 
--section-start=.new.bss=0x1205020,--section-start=.new.comment=0xC04000 

(Lưu ý rằng tôi có tiền tố tên phần với .new sử dụng objcopy --prefix-sections=.new example-embedobj để tránh đụng độ tên phần.)

sau đó tôi đã viết một số mã để tạo ra một tệp thực thi mới (đã mượn một số mã từ cả hai cuốn sách objcopySecurity Warrior). Thực thi mới nên có:

  • Tất cả các phần của example-target và tất cả các phần của example-embed
  • Một bảng biểu tượng trong đó có chứa tất cả những biểu tượng từ example-target và tất cả những biểu tượng của example-embed

Mã Tôi đã viết là:

#include <stdlib.h> 
#include <stdio.h> 
#include <stdbool.h> 
#include <bfd.h> 
#include <libiberty.h> 

struct COPYSECTION_DATA { 
    bfd *  obfd; 
    asymbol ** syms; 
    int  symsize; 
    int  symcount; 
}; 

void copy_section(bfd * ibfd, asection * section, PTR data) 
{ 
    struct COPYSECTION_DATA * csd = data; 
    bfd *    obfd = csd->obfd; 
    asection *  s; 
    long    size, count, sz_reloc; 

    if((bfd_get_section_flags(ibfd, section) & SEC_GROUP) != 0) { 
     return; 
    } 

    /* get output section from input section struct */ 
    s  = section->output_section; 
    /* get sizes for copy */ 
    size  = bfd_get_section_size(section); 
    sz_reloc = bfd_get_reloc_upper_bound(ibfd, section); 

    if(!sz_reloc) { 
     /* no relocations */ 
     bfd_set_reloc(obfd, s, NULL, 0); 
    } else if(sz_reloc > 0) { 
     arelent ** buf; 

     /* build relocations */ 
     buf = xmalloc(sz_reloc); 
     count = bfd_canonicalize_reloc(ibfd, section, buf, csd->syms); 
     /* set relocations for the output section */ 
     bfd_set_reloc(obfd, s, count ? buf : NULL, count); 
     free(buf); 
    } 

    /* get input section contents, set output section contents */ 
    if(section->flags & SEC_HAS_CONTENTS) { 
     bfd_byte * memhunk = NULL; 
     bfd_get_full_section_contents(ibfd, section, &memhunk); 
     bfd_set_section_contents(obfd, s, memhunk, 0, size); 
     free(memhunk); 
    } 
} 

void define_section(bfd * ibfd, asection * section, PTR data) 
{ 
    bfd *  obfd = data; 
    asection * s = bfd_make_section_anyway_with_flags(obfd, 
      section->name, bfd_get_section_flags(ibfd, section)); 
    /* set size to same as ibfd section */ 
    bfd_set_section_size(obfd, s, bfd_section_size(ibfd, section)); 

    /* set vma */ 
    bfd_set_section_vma(obfd, s, bfd_section_vma(ibfd, section)); 
    /* set load address */ 
    s->lma = section->lma; 
    /* set alignment -- the power 2 will be raised to */ 
    bfd_set_section_alignment(obfd, s, 
      bfd_section_alignment(ibfd, section)); 
    s->alignment_power = section->alignment_power; 
    /* link the output section to the input section */ 
    section->output_section = s; 
    section->output_offset = 0; 

    /* copy merge entity size */ 
    s->entsize = section->entsize; 

    /* copy private BFD data from ibfd section to obfd section */ 
    bfd_copy_private_section_data(ibfd, section, obfd, s); 
} 

void merge_symtable(bfd * ibfd, bfd * embedbfd, bfd * obfd, 
     struct COPYSECTION_DATA * csd) 
{ 
    /* set obfd */ 
    csd->obfd  = obfd; 

    /* get required size for both symbol tables and allocate memory */ 
    csd->symsize = bfd_get_symtab_upper_bound(ibfd) /********+ 
      bfd_get_symtab_upper_bound(embedbfd) */; 
    csd->syms  = xmalloc(csd->symsize); 

    csd->symcount = bfd_canonicalize_symtab (ibfd, csd->syms); 
    /******** csd->symcount += bfd_canonicalize_symtab (embedbfd, 
      csd->syms + csd->symcount); */ 

    /* copy merged symbol table to obfd */ 
    bfd_set_symtab(obfd, csd->syms, csd->symcount); 
} 

bool merge_object(bfd * ibfd, bfd * embedbfd, bfd * obfd) 
{ 
    struct COPYSECTION_DATA csd = {0}; 

    if(!ibfd || !embedbfd || !obfd) { 
     return FALSE; 
    } 

    /* set output parameters to ibfd settings */ 
    bfd_set_format(obfd, bfd_get_format(ibfd)); 
    bfd_set_arch_mach(obfd, bfd_get_arch(ibfd), bfd_get_mach(ibfd)); 
    bfd_set_file_flags(obfd, bfd_get_file_flags(ibfd) & 
      bfd_applicable_file_flags(obfd)); 

    /* set the entry point of obfd */ 
    bfd_set_start_address(obfd, bfd_get_start_address(ibfd)); 

    /* define sections for output file */ 
    bfd_map_over_sections(ibfd, define_section, obfd); 
    /******** bfd_map_over_sections(embedbfd, define_section, obfd); */ 

    /* merge private data into obfd */ 
    bfd_merge_private_bfd_data(ibfd, obfd); 
    /******** bfd_merge_private_bfd_data(embedbfd, obfd); */ 

    merge_symtable(ibfd, embedbfd, obfd, &csd); 

    bfd_map_over_sections(ibfd, copy_section, &csd); 
    /******** bfd_map_over_sections(embedbfd, copy_section, &csd); */ 

    free(csd.syms); 
    return TRUE; 
} 

int main(int argc, char **argv) 
{ 
    bfd * ibfd; 
    bfd * embedbfd; 
    bfd * obfd; 

    if(argc != 4) { 
     perror("Usage: infile embedfile outfile\n"); 
     xexit(-1); 
    } 

    bfd_init(); 
    ibfd  = bfd_openr(argv[1], NULL); 
    embedbfd = bfd_openr(argv[2], NULL); 

    if(ibfd == NULL || embedbfd == NULL) { 
     perror("asdfasdf"); 
     xexit(-1); 
    } 

    if(!bfd_check_format(ibfd, bfd_object) || 
      !bfd_check_format(embedbfd, bfd_object)) { 
     perror("File format error"); 
     xexit(-1); 
    } 

    obfd = bfd_openw(argv[3], NULL); 
    bfd_set_format(obfd, bfd_object); 

    if(!(merge_object(ibfd, embedbfd, obfd))) { 
     perror("Error merging input/obj"); 
     xexit(-1); 
    } 

    bfd_close(ibfd); 
    bfd_close(embedbfd); 
    bfd_close(obfd); 
    return EXIT_SUCCESS; 
} 

Để tóm tắt mã này, cần 2 tệp đầu vào ()và embedbfd) để tạo tệp đầu ra (obfd).

  • Bản sao định dạng/arch/mach/cờ tập tin và bắt đầu địa chỉ ibfd-obfd
  • Định nghĩa phần từ cả hai ibfdembedbfd để obfd. Dân số của các phần xảy ra riêng biệt bởi vì BFD yêu cầu rằng tất cả các phần được tạo ra trước khi bất kỳ bắt đầu được dân cư.
  • Hợp nhất dữ liệu riêng tư của cả BFD đầu vào cho đầu ra BFD. Kể từ khi BFD là một trừu tượng phổ biến trên nhiều định dạng tập tin, nó không nhất thiết phải có thể đóng gói toàn diện tất cả mọi thứ theo yêu cầu của định dạng tập tin cơ bản.
  • Tạo bảng biểu tượng kết hợp bao gồm bảng biểu tượng của ibfdembedbfd và đặt làm biểu tượng này là obfd. Bảng biểu tượng này được lưu để sau này có thể được sử dụng để xây dựng thông tin di chuyển.
  • Sao chép các phần từ ibfd đến obfd. Cũng như sao chép nội dung phần, bước này cũng đề cập đến việc xây dựng và thiết lập bảng di chuyển.

Trong mã ở trên, một số dòng được nhận xét với /******** */. Những dòng này đối phó với sự hợp nhất của example-embed. Nếu chúng được nhận xét, điều gì xảy ra là obfd được xây dựng đơn giản dưới dạng bản sao của ibfd. Tôi đã thử nghiệm này và nó hoạt động tốt. Tuy nhiên, một khi tôi nhận xét những dòng này trở lại trong các vấn đề bắt đầu xảy ra.

Với phiên bản chưa được phối hợp thực hiện hợp nhất đầy đủ, nó vẫn tạo tệp đầu ra. Tập tin đầu ra này có thể được kiểm tra với objdump và tìm thấy có tất cả các phần, mã và bảng biểu tượng của cả hai đầu vào. Tuy nhiên, objdump phàn nàn với:

BFD: BFD (GNU Binutils for Ubuntu) 2.21.53.20110810 assertion fail ../../bfd/elf.c:1708 
BFD: BFD (GNU Binutils for Ubuntu) 2.21.53.20110810 assertion fail ../../bfd/elf.c:1708 

Trên hệ thống của tôi, 1708 của elf.c là:

BFD_ASSERT (elf_dynsymtab (abfd) == 0); 

elf_dynsymtab là một macro trong elf-bfd.h cho:

#define elf_dynsymtab(bfd) (elf_tdata(bfd) -> dynsymtab_section) 

Tôi không quen thuộc với lớp ELF, nhưng tôi tin rằng đây là một vấn đề khi đọc bảng biểu tượng động (hoặc có thể nói rằng nó không phải là prese nt). Trong thời gian này, tôi đang cố gắng tránh phải tiếp cận trực tiếp vào lớp ELF trừ khi cần thiết. Có ai có thể cho tôi biết những gì tôi đang làm sai hoặc trong mã của tôi hoặc khái niệm?

Nếu hữu ích, tôi cũng có thể đăng mã để tạo trình tạo liên kết hoặc phiên bản được biên dịch của các tệp nhị phân mẫu.


Tôi nhận thấy đây là một câu hỏi rất lớn và vì lý do này, tôi muốn thưởng cho bất kỳ ai có thể giúp tôi. Nếu tôi có thể giải quyết vấn đề này với sự giúp đỡ của một người nào đó, tôi rất vui khi được tặng thêm 500 điểm thưởng.

+3

Tại sao bạn lại cố gắng thực hiện việc này? Động lực là gì? Bạn có mã nguồn của hai tập tin nhị phân không?Dường như IMHO ngu ngốc. –

+1

@EdHeal Xem câu hỏi được liên kết ở trên cùng với câu hỏi khác của mình, câu hỏi này có một số lý do. –

+0

@EdHeal: Tôi đang tạo một trình soạn thảo thực thi tĩnh, có thể thực hiện một mục tiêu, tiêm các thói quen định nghĩa người dùng vào nó (vai trò của 'example-embed') và sau đó làm tròn mã nhị phân mới để liên kết bản gốc mã vào mã tiêm (Tôi đã viết một công cụ phân tích/phân tích CFG và tôi cũng có thể chỉnh sửa các hướng dẫn tùy ý để tiêm này là mảnh cuối cùng của câu đố). Đối với các usecases tôi cần phải quan tâm, nó có thể được giả định chúng tôi có quyền truy cập vào mã nguồn của thói quen người dùng định nghĩa nhưng không phải là mục tiêu. –

Trả lời

1

Tại sao tất cả điều này theo cách thủ công? Cho rằng bạn có tất cả thông tin biểu tượng (bạn phải chỉnh sửa nhị phân theo cách sane), sẽ không dễ dàng hơn khi SPLIT tệp thực thi thành các tệp đối tượng riêng biệt (ví dụ, một tệp đối tượng cho mỗi hàm), chỉnh sửa và liên kết lại?

+0

Làm cách nào để một tệp thực thi có thể được chia thành một tệp đối tượng mà không có mã nguồn? Tôi có thể giả sử thông tin biểu tượng có sẵn cho đối tượng nhúng, nhưng không cho mục tiêu (mặc dù nếu tôi có thể làm cho nó hoạt động với giả định này trước tiên, điều đó sẽ ổn). –

+0

Các tệp thi hành ELF có thể giữ lại cả thông tin di chuyển và bảng biểu tượng. Khi cả hai phần đều có mặt, việc chia nhỏ tệp thực thi thành các tệp đối tượng là tương đối đơn giản vì bảng biểu tượng cũng cho biết biểu tượng là dữ liệu hay mã. Ngoài ra, tại sao bạn đang cố gắng hợp nhất * thực thi *? Nó sẽ được dễ dàng hơn để tiêm một tập tin đối tượng. – zvrba

+0

Tôi đã cố gắng hợp nhất các tập tin thực thi vì tôi nghĩ rằng nó sẽ dễ dàng hơn vì tôi không còn phải đối phó với việc di chuyển. Tôi đã không thể tìm thấy một cách để tiêm một tập tin đối tượng khi tôi đã cố gắng thay thế. Làm thế nào tôi sẽ đi về chia tách các tập tin thực thi vào các tập tin đối tượng? Cảm ơn sự quan tâm của bạn btw. –