2011-11-08 30 views
5

Tôi muốn sử dụng naked function trong chương trình C++ của mình bằng g ++. Thật không may g ++, không giống như VC++, không hỗ trợ các hàm khỏa thân và cách duy nhất để quản lý việc này là viết mã assembly riêng của bạn trong một tệp riêng biệt và liên kết với các tệp C++ của bạn. Tôi đã cố gắng để tìm thấy một số hướng dẫn tốt cho x86 để trộn các tập tin lắp ráp và C/C++ nhưng không thể tìm thấy bất kỳ tốt nhất.Trộn các tệp C và Assembly

Vui lòng cho tôi biết nếu bạn biết về bất kỳ điều gì. Lưu ý rằng tôi không hỏi về lắp ráp nội tuyến nhưng liên kết các tệp C và lắp ráp và các cách để khai báo biến ngoài của C trong assembly và ngược lại ngoài việc sử dụng chúng trong C hoặc assembly, và cũng có cách liên kết tệp C và asm bằng Makefile .

+0

Hãy thử và làm cho điều này cụ thể hơn - nó khá rộng và mơ hồ khi nó đứng - nếu không nó có thể sẽ bị đóng là "không phải là một câu hỏi thực". Cũng nhớ lại lời khuyên trước đó trong các câu hỏi trước đây của bạn về việc sử dụng 'gcc -S ...' để tạo mẫu asm, v.v. –

+0

gcc trong cùng một mạng có '__attribute __ ((naked))' nhưng không cho x86: ( –

Trả lời

16

Trong C++ file:

extern "C" void foo(); // Stop name mangling games 

int main() { 
    foo(); 
} 

trong file asm "trần truồng", cho x86:

# modified from http://asm.sourceforge.net/howto/hello.html 

.text     # section declaration 
    .global foo 

foo: 

# write our string to stdout 

    movl $len,%edx # third argument: message length 
    movl $msg,%ecx # second argument: pointer to message to write 
    movl $1,%ebx  # first argument: file handle (stdout) 
    movl $4,%eax  # system call number (sys_write) 
    int $0x80  # call kernel 

# and exit 

    movl $0,%ebx  # first argument: exit code 
    movl $1,%eax  # system call number (sys_exit) 
    int $0x80  # call kernel 

.data     # section declaration 

msg: 
    .ascii "Hello, world!\n" # our dear string 
    len = . - msg   # length of our dear string 

Compile, lắp ráp và liên kết (với g ++ chứ không phải là ld bởi vì nó dễ dàng hơn để làm điều đó theo cách đó cho C++) và chạy:

[email protected]:/tmp > g++ -Wall -Wextra test.cc -c -o test.o 
[email protected]:/tmp > as -o asm.o asm.S 
[email protected]:/tmp > g++ test.o asm.o 
[email protected]:/tmp > ./a.out 
Hello, world! 

Rõ ràng nếu bạn muốn chuyển đối số cho hàm của bạn hoặc trả về bất kỳ điều gì y Bạn cần tôn trọng các quy ước gọi điện.

1

Tôi chỉ muốn thêm một thứ vào bài đăng trước đó. Hãy tưởng tượng bạn muốn có một chức năng, chấp nhận đối số: (cái gì đó như

int add(int,int);

prototype)

segment .text 
global add 

    add: 
    enter 0,0 
    mov eax,[ebp+8] ; first argument 
    mov ebx,[ebp+12] ; second argument 
    add eax,ebx 
    leave 
    ret 
+1

yes, ngoại trừ không sử dụng lệnh 'enter' chậm. http://agner.org/optimize/.Không có lý do để xây dựng một khung stack ở đây cả. Chỉ cần 'mov eax, [esp + 4]'/'thêm eax, [esp + 8]'/'ret'. Ngoài ra, không clobber 'ebx'; đó là đăng ký được giữ nguyên trong tất cả các quy ước gọi điện thông thường. Downvoting cho một ví dụ nguy hiểm (và không hiệu quả), và rằng bạn đã không hiển thị như thế nào để xây dựng + liên kết này với NASM. –

5

Dưới đây là một ví dụ về một thủ thuật để đạt được "chức năng khỏa thân" có hiệu lực.

#include <stdio.h> 

extern "C" int naked_func(); 

static void 
dummy() 
{ 
    __asm__ __volatile__ 
    (
    " .global naked_func\n" 
    "naked_func:\n" 
    " movq $3141569, %rax\n" 
    " ret\n" 
    ); 
} 

int 
main() 
{ 
    printf ("%d\n", naked_func()); 
    return 0; 
} 
+2

Bạn thậm chí không cần hàm giả dưới dạng trình bao bọc vì việc lắp ráp nội dòng cơ bản có thể được đặt ở phạm vi toàn cục. –

0

Đây là cách của tôi để xác định một hàm trong assembly, điều này không cần phải có một tệp lắp ráp riêng biệt hoặc bạn không cần phải thoát khỏi mỗi dòng mới. Bạn chỉ có thể sao chép nội dung của các tập tin lắp ráp vào chuỗi chữ. Lưu ý:raw multiline string literal là tính năng C++ 11 (bạn cũng đã gắn thẻ C++). Điều này hữu ích, nếu bạn muốn biên dịch mọi thứ trong một đơn .c -/.cpp -file.

extern"C" int rand_byte(void); 
asm (R"(
    .globl rand_byte 
rand_byte: 
    call rand 
    and eax, 255 
    ret 
)"); 

Bạn chỉ có thể sử dụng câu lệnh lắp ráp cơ bản mà không có tham số bổ sung ở phạm vi toàn cầu. Khi sử dụng GCC hoặc Clang và bộ xử lý cánh tay, bạn có thể sử dụng [[gnu::naked]]/__attribute__((naked)).

[[gnu::naked]] 
int rand_byte(void) { 
    asm volatile (R"(
     push {lr} 
     bl rand 
     and r0, #255 
     pop {pc} 
    )"); 
}; 

Cách thứ nhất luôn cho phép xác định các chức năng khỏa thân. Điều này cũng giúp tạo thêm mã di động.

extern"C" int _cdecl rand_byte(void); 
    #if defined __x86__ 
     // NOTE: the names are decorated with a '_' on windows 32-bit 
     // You can use extern"C" int _cdecl rand_byte() asm("rand_byte"); 
     // to make the asm symbol name always be rand_byte, without an _ 
     asm volatile (R"(
      .globl _rand_byte 
     _rand_byte: 
      call rand 
      and eax, 255 
      ret 
     )"); 
    #elif defined __x86_64__ 
     asm volatile (R"(
      .globl rand_byte 
     rand_byte: 
      call rand 
      and rax, 255 # eax works here, too. x86-32 and -64 could share the same source. 
      ret 
     )"); 
    #elif defined __arm__ 
     asm (R"(
      .global rand_byte 
     rand_byte: 
      push {lr} 
      bl rand 
      and r0, #255 
      pop {pc} 
     )"); 
    #else 
     #error There is no code for your platform yet... 
    #endif 
+0

Bạn có thể sử dụng câu lệnh ASM cơ bản ở phạm vi toàn cục trong GCC/G ++. Bạn không thể sử dụng lắp ráp nội tuyến mở rộng ở phạm vi toàn cầu. Một câu lệnh lắp ráp cơ bản luôn luôn dễ bay hơi trong tài liệu. –

+0

Ví dụ x86 và x86-64 của bạn hơi ngớ ngẩn một chút. 'và eax, 255' thực hiện những gì bạn muốn trên cả x86-32 và x86-64, và là một byte ngắn hơn' và rax, 255'. Ngoài ra, 'movzx eax, al' là tốt hơn một chút trên cả hai, bởi vì nó ngắn hơn. Bạn có chắc chắn điều này thậm chí lắp ráp? Chỉ thị khí là '.globl' hoặc' .global', không phải 'global'. Sử dụng 'gcc -masm = intel' vẫn sử dụng chỉ thị GAS, chứ không phải NASM. (Nó sẽ biên dịch tốt, nhưng không lắp ráp. Trên godbolt, sử dụng chế độ "nhị phân" để đảm bảo nó lắp ráp cũng như biên dịch.) Ngoài ra, ARM không có 'eax', nó có' r0'. –

+0

Tất nhiên, asm cho 'rand_byte' là ngớ ngẩn anyway. Tốt hơn hết là chỉ làm cho 'uint8_t rand_byte' là một bí danh yếu cho' rand' khi quy ước gọi cho phép các giá trị trả về thu hẹp (ví dụ trên x86 nhưng không phải ARM), và cho người gọi nội dòng phần mở rộng bằng 0 nếu cần . Nó gần như không thể đưa ra các ví dụ cho asm nội tuyến, mặc dù nó sẽ không tốt hơn nếu chỉ đơn giản là https://gcc.gnu.org/wiki/DontUseInlineAsm, nhưng bạn có thể muốn ít nhất là đề cập đến nó. (Ngoại trừ các hướng dẫn đặc quyền có thể không có trình bao bọc nội trang/nội tại). –