2011-07-26 22 views
19

Tôi đang xem xét một số mã cũ từ một dự án trường học, và trong việc cố gắng biên dịch nó trên máy tính xách tay của tôi, tôi gặp phải một số vấn đề. Ban đầu nó được viết cho một phiên bản 32 bit cũ của gcc. Dù sao tôi đã cố gắng để chuyển đổi một số lắp ráp trên mã tương thích 64 bit và nhấn một vài snags.Làm thế nào để lưu sổ đăng ký trên x86_64 cho một dịch vụ gián đoạn thường xuyên?

Đây là mã gốc:

pusha 
pushl %ds 
pushl %es 
pushl %fs 
pushl %gs 
pushl %ss 

pusha không hợp lệ trong chế độ 64 bit. Vì vậy, những gì sẽ là cách thích hợp để làm điều này trong x86_64 lắp ráp trong khi ở chế độ 64 bit?

Đã có lý do tại sao pusha không hợp lệ ở chế độ 64 bit, vì vậy tôi có cảm giác đẩy thủ công tất cả thanh ghi có thể không phải là một ý tưởng hay.

Trả lời

7

Tìm hiểu từ mã hiện có thực hiện loại điều này. Ví dụ:

Trên thực tế, "đẩy thủ công" các reg là cách duy nhất trên AMD64 kể từ PUSHA không tồn tại ở đó. AMD64 không phải là duy nhất trong khía cạnh này - hầu hết các CPU không phải x86 đều yêu cầu lưu/khôi phục đăng ký theo từng thời điểm. Nếu bạn kiểm tra mã nguồn được tham chiếu chặt chẽ, bạn sẽ thấy rằng không phải tất cả trình xử lý ngắt đều yêu cầu lưu/khôi phục toàn bộ bộ đăng ký, vì vậy có chỗ cho tối ưu hóa.

+0

Tôi đọc trên [bài viết wiki] (http://en.wikipedia.org/wiki/X86-64) rằng x86-64 tương thích ngược với x86. Sẽ không loại bỏ một break break mà tương thích? –

3

pusha không hợp lệ ở chế độ 64 bit vì nó không cần thiết. Đẩy từng thanh ghi riêng lẻ chính xác là điều cần làm.

+4

Ngoài ra, bạn không thể đẩy đăng ký phân đoạn ở chế độ 64 bit. Bạn cần sao chép chúng vào một thanh ghi khác trước. 'mov% ds,% eax; đẩy% rax'. – ughoavgfhw

+0

@ughoavgfhw Thanh ghi phân đoạn không giữ bất kỳ giá trị có ý nghĩa nào trong chế độ dài. Tất cả các phân đoạn đều có cơ sở bằng 0 và không có giới hạn, ngoại trừ FS và GS, có địa chỉ cơ sở được điều khiển bởi MSR 0xC0000100 và 0xC0000101, được dùng để sử dụng như các con trỏ lưu trữ cục bộ luồng tiện lợi. – doug65536

+1

@ doug65536 Đôi khi vẫn cần thiết để bảo tồn chúng. Ví dụ, một hệ điều hành 64 bit chạy các chương trình 32 bit cần lưu các thanh ghi phân khúc khi nó bị gián đoạn, vì phân đoạn được sử dụng cho chương trình đó. – ughoavgfhw

15

AMD cần một số phòng để thêm mã opcode mới cho tiền tố REX và một số hướng dẫn mới khác khi họ phát triển tiện ích mở rộng x86 64 bit. Họ đã thay đổi ý nghĩa của một số opcodes thành những hướng dẫn mới.

Một số hướng dẫn chỉ đơn giản là các dạng ngắn của các hướng dẫn hiện có hoặc nếu không thì không cần thiết. PUSHA là một trong những nạn nhân. Nó không rõ ràng tại sao họ cấm PUSHA mặc dù, nó dường như không chồng chéo bất kỳ opcodes hướng dẫn mới. Có lẽ chúng được dành riêng cho các mã opcode PUSHAPOPA để sử dụng trong tương lai, vì chúng hoàn toàn thừa và sẽ không nhanh hơn và sẽ không xuất hiện đủ thường xuyên trong mã thành vấn đề.

Trình tự PUSHA là thứ tự của bảng mã lệnh: eax, ecx, edx, ebx, esp, ebp, esi, edi. Lưu ý rằng nó đã đẩy lùi esp! Bạn cần biết esp để tìm dữ liệu được đẩy!

Nếu bạn đang chuyển đổi mã từ 64 bit, mã PUSHA không tốt, bạn cần cập nhật mã để đẩy thanh ghi mới r8 qua r15.Bạn cũng cần lưu và khôi phục trạng thái SSE lớn hơn nhiều, xmm8 qua số xmm15. Giả sử bạn đang đi để clobber chúng.

Nếu mã trình xử lý ngắt chỉ đơn giản là một nhánh để chuyển tiếp tới mã C, bạn không cần phải lưu tất cả các thanh ghi. Bạn có thể giả định rằng trình biên dịch C sẽ tạo mã sẽ được giữ nguyên rbx, rbp, rsi, rdir12 qua r15. Bạn chỉ cần lưu và khôi phục rax, rcx, rdxr8 qua r11. (Lưu ý: trên Linux hoặc các nền tảng System V ABI khác, trình biên dịch sẽ được bảo quản rbx, rbp, r12 - r15, bạn có thể mong đợi rsirdi clobbered).

Thanh ghi phân khúc không giữ giá trị ở chế độ dài (nếu chuỗi bị gián đoạn đang chạy ở chế độ tương thích 32 bit, bạn phải giữ lại thanh ghi phân đoạn, nhờ ughoavgfhw). Trên thực tế, họ đã loại bỏ hầu hết phân đoạn ở chế độ dài, nhưng FS vẫn được dành riêng cho hệ điều hành để sử dụng làm địa chỉ cơ sở cho dữ liệu cục bộ của luồng. Giá trị đăng ký tự nó không quan trọng, cơ sở của FSGS được thiết lập thông qua MSRs 0xC00001000xC0000101. Giả sử bạn sẽ không sử dụng FS bạn không cần phải lo lắng về nó, chỉ cần nhớ rằng bất kỳ luồng dữ liệu cục bộ nào được truy cập bởi mã C có thể sử dụng bất kỳ TLS của luồng ngẫu nhiên nào. Hãy cẩn thận vì thư viện thời gian chạy C sử dụng TLS cho một số chức năng (ví dụ: strtok thường sử dụng TLS).

Tải giá trị vào FS hoặc GS (ngay cả trong chế độ người dùng) sẽ ghi đè FSBASE hoặc GSBASE MSR. Vì một số hệ điều hành sử dụng GS là bộ nhớ "bộ xử lý cục bộ" (chúng cần một cách để có con trỏ đến cấu trúc cho mỗi CPU), chúng cần giữ nó ở một nơi không bị che khuất bằng cách tải GS ở chế độ người dùng. Để giải quyết vấn đề này, có hai MSR được dành riêng cho thanh ghi GSBASE: một hoạt động một và một bị ẩn. Ở chế độ hạt nhân, số GSBASE của hạt nhân được giữ trong MSR GSBASE thông thường và cơ sở chế độ người dùng nằm trong chế độ khác (ẩn) GSBASE MSR. Khi bối cảnh chuyển từ chế độ hạt nhân sang ngữ cảnh chế độ người dùng và khi lưu ngữ cảnh chế độ người dùng và nhập chế độ hạt nhân, mã chuyển đổi ngữ cảnh phải thực hiện lệnh SWAPGS, hoán đổi giá trị của GSBASE MSR hiển thị và ẩn. Vì hạt nhân của GSBASE được ẩn an toàn trong MSR khác trong chế độ người dùng, mã chế độ người dùng không thể che dấu của hạt nhân GSBASE bằng cách tải giá trị vào GS. Khi CPU reenters chế độ hạt nhân, bối cảnh lưu mã sẽ thực hiện SWAPGS và khôi phục của hạt nhân GSBASE.

0

Hi nó có thể không phải là cách chính xác để làm điều đó nhưng người ta có thể tạo các macro như

.macro pushaq 
    push %rax 
    push %rcx 
    push %rdx 
    push %rbx 
    push %rbp 
    push %rsi 
    push %rdi 
.endm # pushaq 

.macro popaq 
    pop %rdi  
    pop %rsi  
    pop %rbp  
    pop %rbx  
    pop %rdx  
    pop %rcx 
    pop %rax 
.endm # popaq 

và cuối cùng thêm thanh ghi r8-15 khác nếu người ta cần đến

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