2013-05-30 52 views
13

TL; DRdi dời Processing ELF - tìm hiểu relocs, biểu tượng, dữ liệu phần, và làm thế nào họ làm việc cùng nhau

tôi đã cố gắng để làm điều này một câu hỏi ngắn nhưng đó là một vấn đề phức tạp nên nó kết thúc lên được lâu. Nếu bạn có thể trả lời bất kỳ phần nào của điều này hoặc đưa ra bất kỳ đề xuất hoặc mẹo hoặc tài nguyên nào, nó sẽ cực kỳ hữu ích (ngay cả khi bạn không trực tiếp giải quyết tất cả các vấn đề của tôi). Tôi đang đập đầu vào tường ngay bây giờ. :)

Dưới đây là các vấn đề cụ thể mà tôi đang gặp phải. Đọc bên dưới để biết thêm thông tin.

  • Tôi đang tìm hướng dẫn về cách xử lý các mục nhập vị trí và cập nhật các ký hiệu chưa được giải quyết trong dữ liệu phần. Tôi chỉ đơn giản là không hiểu phải làm gì với tất cả thông tin tôi đã rút từ các di dời và các phần, v.v.
  • Tôi cũng hy vọng sẽ hiểu những gì đang diễn ra khi mối liên kết gặp phải việc di chuyển. Cố gắng thực hiện chính xác các phương trình di dời và sử dụng tất cả các giá trị chính xác theo cách chính xác là vô cùng khó khăn.
  • Khi tôi gặp mã số và địa chỉ, mã vạch, v.v ..., tôi cần phải hiểu phải làm gì với chúng. Tôi cảm thấy như tôi đang thiếu một số bước.
  • Tôi cảm thấy mình không hiểu rõ về cách các mục trong bảng biểu tượng tương tác với các chuyển vị. Tôi nên sử dụng thông tin ràng buộc, hiển thị, giá trị và kích thước của biểu tượng như thế nào?
  • Cuối cùng, khi tôi xuất tệp của mình với dữ liệu được giải quyết và các mục nhập tái định cư mới được sử dụng bởi tệp thực thi, dữ liệu không chính xác. Tôi không chắc chắn làm thế nào để làm theo tất cả các di dời và cung cấp tất cả các thông tin cần thiết. Việc thực thi mong đợi từ tôi là gì?

cách tiếp cận của tôi cho đến nay

tôi đang cố gắng để tạo ra một tập tin di chuyển trong một cụ thể [không có giấy tờ] định dạng độc quyền được chủ yếu dựa vào ELF. Tôi đã viết một công cụ lấy tệp ELF và một tệp được liên kết một phần (PLF) và xử lý chúng để xuất tệp rel được giải quyết hoàn toàn. Tệp rel này được sử dụng để tải/dỡ dữ liệu khi cần thiết để tiết kiệm bộ nhớ. Nền tảng này là PPC 32 bit. Một nếp nhăn là công cụ được viết cho Windows trong C#, nhưng dữ liệu được dành cho PPC, do đó, có những vấn đề cuối cùng thú vị và muốn xem ra.

Tôi đã cố gắng hiểu cách các chuyển vị được xử lý khi được sử dụng để giải quyết các biểu tượng chưa được giải quyết, v.v. Những gì tôi đã làm cho đến nay là sao chép các phần liên quan từ PLF và sau đó cho mỗi phần .rela tương ứng, tôi phân tích các mục và cố gắng sửa chữa dữ liệu phần và tạo các mục nhập lại mới khi cần thiết. Nhưng đây là nơi khó khăn của tôi. Tôi là cách ra khỏi yếu tố của tôi ở đây và loại điều này dường như thường được thực hiện bởi linkers và loaders vì vậy không có rất nhiều ví dụ tốt để rút ra. Nhưng tôi đã tìm thấy một số đã được giúp đỡ, bao gồm THIS ONE.

Vì vậy, những gì đang xảy ra là:

  1. phần Sao chép dữ liệu từ PLF sẽ được sử dụng cho file rel. Tôi chỉ quan tâm đến .init (không có dữ liệu), .text, .ctors, .dtors, .rodata, .data, .bss (không có dữ liệu) và một phần tùy chỉnh khác mà chúng tôi đang sử dụng.
  2. Lặp lại các phần .rela trong PLF và đọc trong mục nhập Elf32_Rela.
  3. Đối với mỗi mục nhập, tôi kéo các trường r_offset, r_info và r_addend và trích xuất thông tin có liên quan từ r_info (biểu tượng và loại reloc).
  4. Từ bảng biểu tượng của PLF, tôi có thể lấy symbolOffset, symbolSection và symbolValue.
  5. Từ ELF, tôi nhận được địa chỉ tải của symbolSection.
  6. Tôi tính toán int localAddress = (.relaSection.Offset + r_offset).
  7. Tôi nhận được uint relocValue từ nội dung của symbolSection tại r_offset.
  8. Bây giờ tôi có tất cả thông tin tôi cần để tôi thực hiện chuyển đổi trên loại di chuyển và xử lý dữ liệu. Đây là những loại Tôi ủng hộ:
    R_PPC_NONE
    R_PPC_ADDR32
    R_PPC_ADDR24
    R_PPC_ADDR16
    R_PPC_ADDR16_LO
    R_PPC_ADDR16_HI
    R_PPC_ADDR16_HA
    R_PPC_ADDR14
    R_PPC_ADDR14_BRTAKEN
    R_PPC_ADDR14_BRNTAKEN
    R_PPC_REL24
    R_PPC_REL14
    R_PPC_REL14_BRTAKEN
    R_PPC_REL14_BRNTAKEN
  9. Bây giờ cái gì ?? Tôi cần phải cập nhật dữ liệu phần và xây dựng các mục nhập di chuyển đồng hành. Nhưng tôi không hiểu điều gì là cần thiết để làm và làm thế nào để làm điều đó.

Lý do tôi làm điều này là vì có một công cụ cũ không được hỗ trợ cũ không hỗ trợ sử dụng các phần tùy chỉnh, đây là yêu cầu quan trọng cho dự án này (vì lý do bộ nhớ). Chúng tôi có một phần tùy chỉnh có chứa một loạt các mã khởi tạo (tổng cộng khoảng một meg) mà chúng tôi muốn dỡ bỏ sau khi khởi động. Công cụ hiện có chỉ bỏ qua tất cả dữ liệu trong phần đó.

Vì vậy, trong khi làm công cụ riêng của chúng tôi hỗ trợ các phần tùy chỉnh là lý tưởng, nếu có bất kỳ ý tưởng sáng tạo nào khác để đạt được mục tiêu này, tôi là tất cả các tai! Chúng tôi đã lưu hành xung quanh ý tưởng sử dụng phần .dtor cho dữ liệu của chúng tôi vì nó gần như trống. Nhưng điều này là lộn xộn và có thể không hoạt động anyway nếu nó ngăn chặn một tắt máy sạch sẽ.


Relocations cộng mã ví dụ

Khi tôi xử lý di dời, tôi đang làm việc tắt của các phương trình và các thông tin tìm thấy trong các tài liệu ABI HERE (khoảng 4.13 phần, trang 80ish) cũng như một số ví dụ mã và bài đăng trên blog khác mà tôi đã đào tạo. Nhưng đó là tất cả rất khó hiểu và không thực sự đánh vần ra và tất cả các mã tôi đã tìm thấy những điều một chút khác nhau.

Ví dụ,

  • R_PPC_ADDR16_LO -> half16: #lo (S + A)
  • R_PPC_ADDR14_BRTAKEN -> low14 *: (S + A) >> 2
  • vv

Vì vậy, khi tôi thấy loại mã này, làm cách nào để giải mã?

Dưới đây là một ví dụ (từ this source)

case ELF::R_PPC64_ADDR14 : { 
    assert(((Value + Addend) & 3) == 0); 
    // Preserve the AA/LK bits in the branch instruction 
    uint8_t aalk = *(LocalAddress+3); 
    writeInt16BE(LocalAddress + 2, (aalk & 3) | ((Value + Addend) & 0xfffc)); 
} break; 

case ELF::R_PPC64_REL24 : { 
    uint64_t FinalAddress = (Section.LoadAddress + Offset); 
    int32_t delta = static_cast<int32_t>(Value - FinalAddress + Addend); 
    if (SignExtend32<24>(delta) != delta) 
     llvm_unreachable("Relocation R_PPC64_REL24 overflow"); 
    // Generates a 'bl <address>' instruction 
    writeInt32BE(LocalAddress, 0x48000001 | (delta & 0x03FFFFFC)); 
} break; 

Dưới đây là một số từ một ví dụ khác (here)

case R_PPC_ADDR32: /* word32 S + A */ 
    addr = elf_lookup(lf, symidx, 1); 
    if (addr == 0) 
     return -1; 
    addr += addend; 
    *where = addr; 
    break; 

case R_PPC_ADDR16_LO: /* #lo(S) */ 
    if (addend != 0) { 
     addr = relocbase + addend; 
    } else { 
     addr = elf_lookup(lf, symidx, 1); 
     if (addr == 0) 
      return -1; 
    } 
    *hwhere = addr & 0xffff; 
    break; 

case R_PPC_ADDR16_HA: /* #ha(S) */ 
    if (addend != 0) { 
     addr = relocbase + addend; 
    } else { 
     addr = elf_lookup(lf, symidx, 1); 
     if (addr == 0) 
      return -1; 
    } 
    *hwhere = ((addr >> 16) + ((addr & 0x8000) ? 1 : 0)) & 0xffff; 
    break; 

Và một ví dụ khác (from here)

case R_PPC_ADDR16_HA: 
    write_be16 (dso, rela->r_offset, (value + 0x8000) >> 16); 
    break; 
case R_PPC_ADDR24: 
    write_be32 (dso, rela->r_offset, (value & 0x03fffffc) | (read_ube32 (dso, rela->r_offset) & 0xfc000003)); 
    break; 
case R_PPC_ADDR14: 
    write_be32 (dso, rela->r_offset, (value & 0xfffc) | (read_ube32 (dso, rela->r_offset) & 0xffff0003)); 
    break; 
case R_PPC_ADDR14_BRTAKEN: 
case R_PPC_ADDR14_BRNTAKEN: 
    write_be32 (dso, rela->r_offset, (value & 0xfffc) 
            | (read_ube32 (dso, rela->r_offset) & 0xffdf0003) 
            | ((((GELF_R_TYPE (rela->r_info) == R_PPC_ADDR14_BRTAKEN) << 21) 
            ^(value >> 10)) & 0x00200000)); 
    break; 
case R_PPC_REL24: 
    write_be32 (dso, rela->r_offset, ((value - rela->r_offset) & 0x03fffffc) | (read_ube32 (dso, rela->r_offset) & 0xfc000003)); 
    break; 
case R_PPC_REL32: 
    write_be32 (dso, rela->r_offset, value - rela->r_offset); 
    break; 

Tôi thực sự muốn để hiểu được sự kỳ diệu của những kẻ này g ở đây và tại sao mã của họ không phải lúc nào cũng giống nhau. Tôi nghĩ rằng một số mã đang đưa ra các giả định rằng dữ liệu đã được che dấu đúng cách (đối với các nhánh, vv) và một số mã không phải là. Nhưng tôi không hiểu gì về điều này cả.


Sau những biểu tượng/data/di dời, vv

Khi tôi nhìn vào các dữ liệu trong một HexEditor, tôi thấy một loạt các "48 00 00 01" khắp nơi. Tôi đã tìm ra rằng đây là một opcode và cần phải được cập nhật thông tin di dời (đặc biệt là dành cho chi nhánh và liên kết 'bl'), nhưng công cụ của tôi không hoạt động trên phần lớn chúng và những thứ tôi làm cập nhật có giá trị sai trong chúng (so với một ví dụ được thực hiện bởi một công cụ lỗi thời). Rõ ràng tôi đang thiếu một số phần của quá trình.

Ngoài dữ liệu phần, có các mục nhập bổ sung cần phải được thêm vào cuối tệp rel. Chúng bao gồm các di dời nội bộ và bên ngoài nhưng tôi chưa tìm hiểu nhiều về những điều này. (sự khác nhau giữa hai và khi nào bạn sử dụng cái này hay cái kia?)

Nếu bạn nhìn gần cuối this file tại hàm RuntimeDyldELF::processRelocationRef, bạn sẽ thấy một số Mục nhập lại được tạo. Họ cũng làm cho các chức năng sơ khai. Tôi nghi ngờ đây là liên kết còn thiếu cho tôi, nhưng rõ ràng là bùn và tôi không theo dõi nó một chút.

Khi tôi xuất các ký hiệu trong mỗi mục nhập lại, mỗi mục có một ràng buộc/khả năng hiển thị [Toàn cầu/Yếu/cục bộ] [Chức năng/đối tượng] và một giá trị, kích thước và một phần. Tôi biết phần là nơi biểu tượng được đặt, và giá trị là bù đắp cho các biểu tượng trong phần đó (hoặc là nó địa chỉ ảo?). Kích thước là kích thước của biểu tượng, nhưng điều này có quan trọng không? Có lẽ toàn cầu/yếu/địa phương là hữu ích để xác định nếu đó là một di chuyển nội bộ hoặc bên ngoài?

Có lẽ bảng di dời này tôi đang nói về việc tạo ra thực sự là một bảng biểu tượng cho tệp rel của tôi? Có lẽ bảng này cập nhật giá trị biểu tượng từ một địa chỉ ảo thành một phần bù đắp (vì đó là giá trị trong các tệp có thể định vị lại và bảng biểu tượng trong PLF về cơ bản là một tệp thực thi)?


Một số nguồn:

  1. blog trên di dời: http://eli.thegreenplace.net/2011/08/25/load-time-relocation-of-shared-libraries/
  2. đề cập opcodes ở cuối: http://wiki.netbsd.org/examples/elf_executables_for_powerpc/
  3. liên quan câu hỏi chưa được trả lời của tôi: ELF Relocation reverse engineering

Whew! Đó là một con thú của một câu hỏi. Xin chúc mừng nếu bạn đã thực hiện điều này ở xa. :) Cảm ơn trước cho bất kỳ sự giúp đỡ nào bạn có thể cho tôi.

+0

bản sao có thể có của [Khái niệm di chuyển] (http://stackoverflow.com/questions/16385826/concept-of-relocation) Ví dụ tối thiểu chi tiết tại: http://stackoverflow.com/a/30507725/895245 –

Trả lời

14

Tôi tình cờ gặp câu hỏi này và nghĩ rằng nó xứng đáng là câu trả lời.

Có tiện ích. Bạn có thể tìm thấy nó trên internet.

Mỗi phần RELA chứa một mảng các mục nhập Elf32_Rela như bạn biết, nhưng cũng được gắn với một phần nhất định khác. r_offset là phần bù vào phần kia (trong trường hợp này là it works differently for shared libraries). Bạn sẽ thấy rằng tiêu đề phần có một thành viên được gọi là sh_info. Điều này cho bạn biết phần nào. (Đó là một chỉ mục trong bảng tiêu đề phần như bạn mong đợi.)

'Ký hiệu' mà bạn nhận được từ r_info thực tế là một chỉ mục trong bảng biểu tượng nằm trong phần khác. Tìm kiếm sh_link thành viên trong tiêu đề của phần RELA của bạn.

Bảng biểu tượng cho bạn biết tên của biểu tượng bạn đang tìm kiếm, dưới dạng thành viên st_name của Elf32_Sym. st_name là phần bù vào phần chuỗi. Phần nào, bạn nhận được từ thành viên sh_link của phần đầu bảng của biểu tượng của bạn. Xin lỗi nếu điều này gây nhầm lẫn.

Elf32_Shdr *sh_table = elf_image + ((Elf32_Ehdr *)elf_image)->e_shoff; 
Elf32_Rela *relocs = elf_image + sh_table[relocation_section_index]->sh_offset; 

unsigned section_to_modify_index = sh_table[relocation_section_index].sh_info; 
char *to_modify = elf_image + sh_table[section_to_modify_index].sh_offset; 

unsigned symbol_table_index = sh_table[relocation_section_index].sh_link; 
Elf32_Sym *symbol_table = elf_image + sh_table[symbol_table_index].sh_offset; 

unsigned string_table_index = sh_table[symbol_table].sh_link; 
char *string_table = elf_image + sh_table[string_table_index].sh_offset; 

Giả sử chúng tôi đang làm việc với số di dời i.

Elf32_Rela *rel = &relocs[i]; 
Elf32_Sym *sym = &symbol_table[ELF32_R_SYM(rel->r_info)]; 
char *symbol_name = string_table + sym->st_name; 

Tìm địa chỉ của biểu tượng đó (giả sử symbol_name == "printf"). Giá trị cuối cùng sẽ vào (to_modify + rel-> r_offset).

Đối với bảng trên trang 79-83 của pdf bạn đã liên kết, nó cho chúng tôi biết phải đặt gì ở địa chỉ đó và số byte cần ghi. Rõ ràng là địa chỉ chúng ta vừa có (của printf trong trường hợp này) là một phần của hầu hết chúng. Nó tương ứng với S trong các biểu thức.

r_addend chỉ là A. Đôi khi trình biên dịch cần phải thêm một hằng số tĩnh vào một reloc tôi đoán.

B là địa chỉ cơ sở của đối tượng được chia sẻ hoặc 0 cho các chương trình thực thi vì chúng không được di chuyển.

Vì vậy, nếu ELF32_R_TYPE (rel-> r_info) == R_PPC_ADDR32 chúng tôi có S + A, và kích thước chữ là word32 vì vậy chúng tôi sẽ nhận được:

*(uint32_t *)(to_modify + rel->r_offset) = address_of_printf + rel->r_addend; 

... và chúng tôi đã thực hiện thành công một di dời .

Tôi không thể giúp bạn khi nói đến #lo, #hi, v.v ... và kích thước từ như low14. Tôi không biết gì về UBND tỉnh nhưng pdf liên kết có vẻ hợp lý đủ.

Tôi cũng không biết về các chức năng gốc. Bạn thường không cần biết về những người khi liên kết (ít nhất là động).

Tôi không chắc chắn liệu tôi có trả lời tất cả các câu hỏi của bạn hay không nhưng bạn sẽ có thể xem mã ví dụ của bạn hiện giờ ít nhất.

1

Cố gắng llok vào Đặc điểm kỹ thuật ELF. Nó mất khoảng 60 trang, và làm rõ rất nhiều điều. Đặc biệt là phần 2, một về liên kết.

+0

Hoặc , tốt hơn, Hệ thống V ABI (cả hai loại chung chung (gABI) và bổ sung kiến ​​trúc cụ thể. Tôi thấy hữu ích: https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI (cho x86/x86-64) và http://www.sco.com/developers/gabi/ –

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