2011-11-14 17 views
10

Theo dõi Why is the ELF execution entry point virtual address of the form 0x80xxxxx and not zero 0x0?Why do virtual memory addresses for linux binaries start at 0x8048000?, tại sao tôi không thể thực hiện ld sử dụng điểm nhập khác với điểm nhập mặc định với ld -e?Tại sao điểm truy cập ELF 0x8048000 không thay đổi được với tùy chọn "ld -e"?

Nếu tôi làm như vậy, tôi có thể nhận được segmentation fault với mã trả lại 139, ngay cả đối với địa chỉ gần điểm nhập mặc định. Tại sao?

EDIT:

tôi sẽ làm cho các câu hỏi cụ thể hơn:

 .text 
     .globl _start  
_start: 
     movl $0x4,%eax  # eax = code for 'write' system call 
     movl $1,%ebx   # ebx = file descriptor to standard output 
     movl $message,%ecx # ecx = pointer to the message 
     movl $13,%edx   # edx = length of the message 
     int $0x80   # make the system call 
     movl $0x0,%ebx  # the status returned by 'exit' 
     movl $0x1,%eax  # eax = code for 'exit' system call 
     int $0x80   # make the system call 
     .data 
     .globl message 
message:   
     .string "Hello world\n" # The message as data 

Nếu tôi biên dịch này với as program.s -o program.o và sau đó liên kết nó tĩnh với ld -N program.o -o program, readelf -l program show 0x0000000000400078 như VirtAddr của văn bản phân đoạn và 0x400078 làm điểm vào. Khi chạy, `Hello world 'được in.

Tuy nhiên, khi tôi cố liên kết với ld -N -e0x400082 -Ttext=0x400082 program.o -o program (phân đoạn văn bản di chuyển và điểm vào 4 byte), chương trình sẽ là killed bây giờ hiển thị hai tiêu đề khác nhau kiểu LOAD, một ở 0x0000000000400082 và một ở 0x00000000004000b0.

Khi tôi cố gắng 0x400086, tất cả các công trình, và chỉ có một LOAD phần.

  1. gì đang xảy ra ở đây?
  2. Tôi có thể chọn địa chỉ bộ nhớ nào, những địa chỉ nào tôi không thể chọn và tại sao?

Cảm ơn bạn.

+0

Tôi cũng đã có thể sửa đổi các điểm nhập với kịch bản mối liên kết: http://stackoverflow.com/a/30536800/895245 –

Trả lời

24

lý do tại sao tôi không thể làm cho ld sử dụng một điểm mấu chốt khác so với mặc định với ld -e

Bạn chắc chắn có thể. Điều này:

int foo(int argc, char *argv[]) { return 0; } 

gcc main.c -Wl,-e,foo 

sẽ không hoạt động, bởi vì quá trình thực thi không bắt đầu chính. Nó bắt đầu tại _start, được liên kết từ crt0.o (một phần của glibc) và sắp xếp cho những thứ như liên kết động, v.v. để khởi động đúng cách. Bằng cách chuyển hướng _start đến foo, bạn đã bỏ qua tất cả yêu cầu khởi tạo glibc và do đó mọi thứ không hoạt động.

Nhưng nếu bạn không cần liên kết động, và sẵn sàng làm những gì glibc thường làm cho bạn, thì bạn có thể đặt tên cho điểm vào bất cứ điều gì bạn muốn. Ví dụ:

#include <syscall.h> 

int foo() 
{ 
    syscall(SYS_write, 1, "Hello, world\n", 13); 
    syscall(SYS_exit, 0); 
} 

gcc t.c -static -nostartfiles -Wl,-e,foo && ./a.out 
Hello, world 

Ồ, và tiêu đề của câu hỏi này không khớp với câu hỏi thực tế của bạn (ý tưởng tồi).

Để trả lời câu hỏi trong tiêu đề, bạn chắc chắn có thể thay đổi địa chỉ mà tệp thi hành của bạn được liên kết tại. Theo mặc định, bạn nhận được 0x8048000 địa chỉ tải (chỉ trong 32 bit; mặc định 64 bit là 0x400000).

Bạn có thể dễ dàng thay đổi điều đó thành ví dụ:0x80000 bằng cách thêm -Wl,-Ttext-segment=0x80000 vào dòng liên kết.

Cập nhật:

Tuy nhiên, khi tôi cố gắng liên kết với ld -N -e0x400082 -Ttext = 0x400082 program.o -o chương trình (di chuyển đoạn văn bản và điểm vào 4 byte), chương trình sẽ bị giết.

Vâng, nó là không thể gán Ttext để 0x400082 mà không vi phạm .text phần liên kết hạn chế (đó là 4). Bạn phải giữ địa chỉ .text được canh lề trên ít nhất 4 byte (hoặc thay đổi căn chỉnh theo yêu cầu của .text).

Khi tôi đặt địa chỉ bắt đầu thành 0x400078, 0x40007c, 0x400080, 0x400084, ..., 0x400098 và sử dụng GNU-ld 2.20.1, chương trình hoạt động.

Tuy nhiên, khi tôi sử dụng ảnh chụp CVS hiện tại của binutils, chương trình hoạt động cho 0x400078, 0x40007c, 0x400088, 0x40008c, và bị giết cho 0x400080, 0x400084, 0x400090, 0x400094, 0x400098. Đây có thể là một lỗi trong trình liên kết, hoặc tôi vi phạm một số ràng buộc khác (tôi không thấy mặc dù).

Tại thời điểm này, nếu bạn đang thực sự quan tâm, tôi đề nghị tải nguồn binutils, xây dựng ld, và tìm ra chính xác những gì gây ra nó để tạo ra hai phân khúc PT_LOAD thay vì một.

Cập nhật 2:

Force phân khúc mới cho phần với LMAs chồng chéo.

Ah! Điều đó chỉ có nghĩa là bạn cần di chuyển .data ra khỏi đường. Điều này làm cho một thực thi làm việc:

ld -N -o t t.o -e0x400080 -Ttext=0x400080 -Tdata=0x400180 
+0

tôi cập nhật câu hỏi của tôi để thực hiện một ví dụ tốt hơn của những gì không hoạt động như mong đợi. – nh2

+0

Cảm ơn bạn, câu trả lời tuyệt vời, tôi không xem xét sự liên kết. – nh2

+0

Tôi đã sử dụng git bisect để tìm sau đó thay đổi giữa các binutils 2,20 và 2,21 giới thiệu thay đổi bạn mô tả. Nó được gọi là "elf.c (_bfd_elf_map_sections_to_segments): Buộc phân đoạn mới cho các phần có LMA trùng lặp". (http://repo.or.cz/w/binutils.git/commit/278c98e2ff1c95c8ad9579755abda467ea2bc1b4) – nh2

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