2012-05-07 7 views
39

Tôi đang lập trình phía máy chủ của một hệ thống máy gia tốc. Máy chủ chạy trên PC dưới Ubuntu Linux và giao tiếp với phần cứng được nhúng thông qua kết nối USB. Giao tiếp được thực hiện bằng cách sao chép các khối bộ nhớ đến và từ bộ nhớ của phần cứng được nhúng.Lỗi GCC này "... di dời cắt ngắn để phù hợp ..." có nghĩa là gì?

Trên bộ nhớ của bảng, có vùng bộ nhớ mà tôi sử dụng làm hộp thư nơi tôi viết và đọc dữ liệu. Hộp thư được định nghĩa là một cấu trúc và tôi sử dụng cùng một định nghĩa để cấp phát một hộp thư gương trong không gian lưu trữ của tôi.

Tôi đã sử dụng kỹ thuật này thành công trong quá khứ vì vậy bây giờ tôi đã sao chép dự án Eclipse máy chủ vào không gian làm việc của dự án hiện tại của tôi và thực hiện các thay đổi tên thích hợp. Điều kỳ lạ là khi xây dựng các dự án chủ bây giờ tôi nhận được thông báo sau:

Xây dựng mục tiêu: fft2d_host
Gọi: GCC C Linker
gcc -L/opt/adapteva/esdk/tools/host/x86_64/lib -o "fft2d_host" ./src/fft2d_host.o -le_host -lrt

./src/fft2d_host.o: Trong chức năng 'chính.

fft2d_host.c :(văn bản + 0x280): di chuyển cắt ngắn để vừa với: R_X86_64_PC32 dựa vào ký hiệu 'Hộp thư' được định nghĩa trong phần COMMON trong ./src/fft2d_host.o

Lỗi này có ý nghĩa gì và tại sao nó không được xây dựng trên dự án hiện tại, trong khi nó phù hợp với dự án cũ hơn?

Trả lời

39

Bạn đang cố gắng liên kết dự án của mình theo cách mà mục tiêu của một lược đồ địa chỉ tương đối xa hơn có thể được hỗ trợ với dịch chuyển 32 bit của chế độ địa chỉ liên quan đã chọn. Điều này có thể là do dự án hiện tại lớn hơn, bởi vì nó liên kết các tệp đối tượng theo một thứ tự khác hoặc bởi vì có một sơ đồ ánh xạ mở rộng không cần thiết trong khi chơi.

Câu hỏi này là một ví dụ hoàn hảo về việc tại sao nó thường hiệu quả để thực hiện tìm kiếm web trên phần chung của một thông báo lỗi - bạn tìm thấy những thứ như thế này:

http://www.technovelty.org/code/c/relocation-truncated.html

nào đưa ra một số gợi ý chữa bệnh.

+2

Đây là một gợi ý: Bạn có thể vô tình xây dựng (các) đối tượng 64 bit mà không có '-fPIC'. Điều đó đã vấp ngã tôi một lúc. –

6

Hãy nhớ xử lý các thông báo lỗi theo thứ tự. Trong trường hợp của tôi, lỗi trên đây là "tham chiếu không xác định" và tôi đã bỏ qua nó một cách trực quan đến lỗi "di chuyển bị cắt ngắn" thú vị hơn. Trong thực tế, vấn đề của tôi là một thư viện cũ đã gây ra thông báo "không xác định tham chiếu". Một khi tôi đã khắc phục điều đó, "di dời cắt ngắn" cũng biến mất.

+0

tôi cũng vậy: sự cố đã lỗi thời .o tệp. Tiêu đề được đề cập đến các chức năng không có ở đó. Biên dịch lại và không sao, tôi đoán mối liên kết trong những tình huống này quyết định vị trí "rất lớn" thay vì "không tồn tại" :) – robert

13

dụ tối thiểu mà tạo ra các lỗi

main.S: di chuyển một địa chỉ vào %eax (32-bit):

_start: 
    mov $_start, %eax 

linker.ld:

SECTIONS 
{ 
    /* This says where `.text` will go in the executable. */ 
    . = 0x100000000; 
    .text : 
    { 
     *(*) 
    } 
} 

Compile trên x86 -64:

as -o main.o main.S 
ld -o main.out -T linker.ld main.o 

Kết quả của ld:

(.text+0x1): relocation truncated to fit: R_X86_64_32 against `.text' 

Hãy ghi nhớ rằng:

  • as đặt mọi thứ trên .text nếu không có phần khác được quy định
  • ld sử dụng .text như điểm vào mặc định nếu ENTRY. Do đó _start là byte đầu tiên của .text.

Làm thế nào để sửa chữa nó: sử dụng linker.ld này để thay thế, và trừ 1 từ đầu:

SECTIONS 
{ 
    . = 0xFFFFFFFF; 
    .text : 
    { 
     *(*) 
    } 
} 

Ghi chú:

  • chúng ta không thể làm cho _start toàn cầu trong ví dụ này với .global _start, nếu không nó vẫn không thành công. Tôi nghĩ điều này xảy ra bởi vì các biểu tượng toàn cầu có các ràng buộc liên kết (0xFFFFFFF0 hoạt động). TODO được ghi chép trong tiêu chuẩn ELF ở đâu?

  • đoạn .text cũng có ràng buộc căn chỉnh là p_align == 2M. Nhưng người liên kết của chúng tôi đủ thông minh để đặt phân đoạn tại 0xFFE00000, điền số 0 cho đến 0xFFFFFFFF và đặt e_entry == 0xFFFFFFFF. Điều này làm việc, nhưng tạo ra một thực thi quá khổ.

Thử nghiệm trên Ubuntu 14.04 AMD64, Binutils 2.24.

Giải thích

Trước tiên, bạn phải hiểu những gì di dời là với một ví dụ nhỏ: https://stackoverflow.com/a/30507725/895245

Tiếp theo, hãy nhìn vào objdump -Sr main.o:

0000000000000000 <_start>: 
    0: b8 00 00 00 00   mov $0x0,%eax 
         1: R_X86_64_32 .text 

Nếu chúng ta nhìn vào cách hướng dẫn là được mã hóa trong sách hướng dẫn của Intel, chúng tôi thấy rằng:

  • b8 nói rằng đây là một mov để %eax
  • 0 là một giá trị ngay lập tức được chuyển đến %eax. Việc di dời sau đó sẽ sửa đổi nó để chứa địa chỉ của _start.

Khi di chuyển đến thanh ghi 32 bit, ngay lập tức cũng phải là 32 bit.

Nhưng tại đây, việc di dời phải sửa đổi 32 bit đó để đặt địa chỉ _start vào chúng sau khi liên kết diễn ra.

0x100000000 không vừa với 32 bit, nhưng 0xFFFFFFFF thì có. Vì vậy, lỗi.

Lỗi này chỉ có thể xảy ra đối với các chuyển vị tạo ra sự cắt ngắn, ví dụ: R_X86_64_32 (8 byte đến 4 byte), nhưng không bao giờ trên R_X86_64_64.

Và có một số loại di chuyển yêu cầu tiện ích mở rộng thay vì tiện ích mở rộng bằng 0 như được hiển thị tại đây, ví dụ: R_X86_64_32S. Xem thêm: https://stackoverflow.com/a/33289761/895245

7

Tôi đã gặp sự cố này khi xây dựng một chương trình yêu cầu lượng không gian ngăn xếp lớn (trên 2 GiB). Giải pháp là thêm cờ -mcmodel=medium, được hỗ trợ bởi cả trình biên dịch GCC và Intel.

+2

Tôi xác nhận điều này. Bạn cũng không phải biên dịch nó như một thư viện bằng '-fPIC': https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/ topic/268374 # comment-1590873 –

+1

Trong trường hợp điều này không rõ ràng, ** không sử dụng '-mcmodel = medium' nếu bạn không phải **, bởi vì nó làm cho asm kém hiệu quả hơn khi xử lý lớn ('-ml-data-threshold' mặc định là 64kiB) các mảng tĩnh/toàn cục. Hãy tìm các lý do khác trước, ví dụ: thử '-fPIC'. Nó không rõ ràng lý do tại sao hơn 2GB * stack * sẽ không tương thích với mặc định '-mcmodel = small', vì các biểu tượng toàn cục không tham chiếu đến bộ nhớ ngăn xếp, và ngăn xếp đã nằm ngoài 2GiB thấp cho bình thường (' -mcmodel = small') thực thi. Xem https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html. –

5

Trên Cygwin -mcmodel=medium đã được đặt mặc định và không hữu ích. Để tôi thêm -Wl,--image-base -Wl,0x10000000 vào GCC linker đã sửa lỗi.

+0

Điều đó phù hợp với tôi. Sao bạn tìm hiểu ra được? – garyp

3

Thông thường, lỗi này có nghĩa là chương trình của bạn quá lớn và thường quá lớn vì nó chứa một hoặc nhiều đối tượng dữ liệu rất lớn. Ví dụ:

char large_array[1ul << 31]; 
int other_global; 
int main(void) { return other_global; } 

sẽ tạo ra lỗi "di dời cắt ngắn để phù hợp" trên x86-64/Linux, nếu được biên dịch ở chế độ mặc định và không tối ưu hóa. (Nếu bạn bật tối ưu hóa, nó có thể, ít nhất về mặt lý thuyết, tìm ra rằng large_array không được sử dụng và/hoặc rằng other_global không bao giờ được viết và do đó tạo mã không kích hoạt vấn đề.)

Điều gì đang xảy ra theo mặc định, GCC sử dụng "mô hình mã nhỏ" của nó trên kiến ​​trúc này, trong đó tất cả mã của chương trình và dữ liệu phân bổ tĩnh phải phù hợp với 2GB không gian địa chỉ thấp nhất. (Giới hạn trên chính xác là 2GB - 2MB, vì 2MB thấp nhất của không gian địa chỉ của bất kỳ chương trình nào là vĩnh viễn không sử dụng được.Nếu bạn đang biên soạn một thư viện được chia sẻ hoặc thực thi độc lập với vị trí, tất cả mã và dữ liệu vẫn phải phù hợp với hai gigabyte, nhưng chúng không được đóng đinh ở dưới cùng của không gian địa chỉ nữa.) large_array tiêu thụ toàn bộ không gian đó , do đó, other_global được gán một địa chỉ vượt quá giới hạn và mã được tạo cho main không thể truy cập được. Bạn nhận được một lỗi khó hiểu từ trình liên kết, chứ không phải là lỗi "large_array quá lớn" hữu ích từ trình biên dịch, bởi vì trong các trường hợp phức tạp hơn, trình biên dịch không thể biết rằng other_global sẽ nằm ngoài tầm với, vì vậy thậm chí không thử cho các trường hợp đơn giản.

Hầu hết thời gian, phản hồi chính xác để nhận lỗi này là cấu trúc lại chương trình của bạn để không cần mảng tĩnh khổng lồ và/hoặc gigabyte mã máy. Tuy nhiên, nếu bạn thực sự phải có chúng vì lý do nào đó, bạn có thể sử dụng "medium" or "large" code models để nâng giới hạn, với giá của việc tạo mã kém hiệu quả hơn một chút. Các mô hình mã này đặc biệt x86-64; một cái gì đó tương tự tồn tại đối với hầu hết các kiến ​​trúc khác, nhưng tập hợp chính xác "mô hình" và các giới hạn liên quan sẽ khác nhau. (Ví dụ: trên kiến ​​trúc 32 bit, bạn có thể có một mô hình "nhỏ" trong đó tổng số lượng mã và dữ liệu bị giới hạn ở một số thứ như 2 byte.)

+0

Cảm ơn. Như đã lưu ý trong câu hỏi, đây không phải là mã x86. Đây là một thời gian dài trước đây, nhưng dù sao, tôi không chắc chắn như thế nào kích thước mô hình mã có liên quan đến một bộ vi xử lý nhúng? Không phải là giới hạn/tài sản cụ thể x86 này? – ysap

+0

@ysap Mỗi kiến ​​trúc CPU có những hạn chế _like_ điều này - thường được thiết lập bởi số lượng bit phù hợp với toán hạng tức thời của một số lệnh máy. Tôi đã viết câu trả lời này vì một câu hỏi mới hơn đã bị đóng như một bản sao của câu hỏi này và tôi không nghĩ rằng các câu trả lời hiện có giải quyết vấn đề của người đó rất tốt và tôi chỉ sử dụng x86-64 làm ví dụ thuận tiện. (Tuy nhiên, thông báo lỗi trong câu hỏi có chứa thuật ngữ 'R_X86_64_PC32', chắc chắn có vẻ như bạn đang biên dịch mã cho x86-64. Có lẽ vấn đề thực sự của bạn là makefile của bạn không gọi trình biên dịch chéo mà nó cần phải có.) – zwol

+0

Tôi đã được biên dịch cho chắc chắn, nhưng hey, ngay cả công ty đó không còn tồn tại ... Cảm ơn cho đầu vào. – ysap