2011-11-08 85 views
5

我想在我的C++程序中使用g ++來使用naked function。不幸的是,和VC++不同的是,g ++不支持裸函數,管理這個函數的唯一方法是將自己的彙編代碼寫入單獨的文件並與C++文件鏈接。我試圖找到一些很好的x86教程,以混合程序集和C/C++文件,但找不到任何好的教程。混合C和彙編文件

請讓我知道,如果你知道任何。請注意,我沒有詢問內聯彙編,而是鏈接C和彙編文件,以及如何在彙編中聲明C的extern變量,反之亦然,除了在C或彙編中使用它們,還有使用Makefile鏈接C和彙編文件的方法。

+0

試着讓這個更具體 - 它的含義相當寬泛和模糊 - 否則它可能會被視爲「不是真正的問題」。也請回顧一下早先關於使用'gcc -S ...'生成asm模板等問題的建議。 –

+0

gcc以同樣的方式擁有'__attribute __((naked))',但不適用於x86 :( –

回答

16

在C++中的文件:

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

int main() { 
    foo(); 
} 
在 「裸」 ASM文件

,用於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 

編譯,彙編和鏈接(使用g ++而不是LD,因爲它更容易做到這一點這種方式爲C + +)和運行:

[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! 

顯然,如果你想傳遞參數到你的函數或返回任何東西y你需要尊重呼叫公約。

1

我只想添加一件事情到以前的帖子。 想象一下,你想要的功能,接受參數: (類似

int add(int,int);

原型)

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

是的,除了不要使用緩慢的'enter'指令。http://agner.org/optimize/。沒有理由在這裏建立一個堆棧框架。只要'mov eax,[esp + 4]'/'添加eax,[esp + 8]'/'ret'。另外,不要打破'ebx';它是所有常用調用約定中的一個調用保存的寄存器。爲一個危險的(低效率的)例子下降,並且你沒有展示如何構建+將它與NASM鏈接。 –

5

這裏有一個竅門,以實現「裸功能」作用的一個例子。

#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

您甚至不需要虛函數作爲包裝器,因爲可以將基本內聯裝配放置在全局範圍內。 –

0

這是我在彙編中定義函數的方法,這不需要單獨的彙編程序文件,也不需要轉義每一個換行符。您可以將程序集文件的內容複製到字符串文字中。 注意:raw multiline string literal是一個C++ 11功能(您也標記爲C++)。如果要編譯單個文件中的所有內容,這非常有用。

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

您只能使用一個基本的彙編語句沒有在全球範圍內的其他參數。 使用GCC或Clang和手臂處理器時,您可以使用[[gnu::naked]]/__attribute__((naked))

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

第一種方式總是允許定義裸露功能。 這也有助於製作更多可移植的代碼。

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

您可以在GCC/G ++的全局範圍內使用基本的ASM語句。您不能在全局範圍內使用擴展內聯彙編。每個文檔都有一個基本的彙編語句隱含地變化。 –

+0

你的x86與x86-64的例子有點傻。 '和eax,255'在x86-32和x86-64上都是你想要的,比'和rax,255'短一個字節。另外,'movzx eax,al'稍好,因爲它更短。你確定這甚至組裝? gas指令是'.globl'或'.global',而不是'global'。使用'gcc -masm = intel'仍然使用GAS指令,而不是NASM。 (它可以很好地編譯,但不能彙編,在godbolt上,使用「二進制」模式來確保彙編和編譯。)另外,ARM沒有'eax',它有'r0'。 –

+0

當然,'rand_byte'的asm無論如何都是愚蠢的。當調用約定允許窄的返回值具有較高的垃圾(像x86而不是ARM)時,只需將'uint8_t rand_byte'設置爲'rand'的弱別名就更好了,並且讓調用者在需要時內聯零擴展。幾乎不可能拿出內聯asm的例子,儘管簡單地https://gcc.gnu.org/wiki/DontUseInlineAsm並不會更好,但是您可能至少要提及它。 (除了可能沒有內置/內部包裝的特權指令)。 –