Смешивание файлов C и Assembly - PullRequest
8 голосов
/ 08 ноября 2011

Я хочу использовать голую функцию в моей программе на C ++ с использованием g ++. К сожалению, g ++, в отличие от VC ++, не поддерживает «голые» функции, и единственный способ справиться с этим - написать собственный код сборки в отдельном файле и связать его с файлами C ++. Я пытался найти хороший учебник для x86, чтобы смешать файлы ассемблера и C / C ++, но не смог найти хорошего.

Пожалуйста, дайте мне знать, если вы знаете о чем-либо. Обратите внимание, что я не спрашиваю о встроенной сборке, но связываю файлы C и сборки и способы объявления внешних переменных C в сборке и наоборот, кроме использования их в C или сборке, а также о способах связывания файлов C и asm с использованием Makefile. .

Ответы [ 4 ]

17 голосов
/ 08 ноября 2011

В файле 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 ++) и запустить:

ajw@rapunzel:/tmp > g++ -Wall -Wextra test.cc -c -o test.o
ajw@rapunzel:/tmp > as -o asm.o asm.S
ajw@rapunzel:/tmp > g++ test.o asm.o
ajw@rapunzel:/tmp > ./a.out
Hello, world!

Если вы хотите передать аргументы своей функции или вернуть что-либо, вам нужно соблюдать соглашения о вызовах.

5 голосов
/ 12 ноября 2011

Вот пример уловки для достижения эффекта «голой функции».

#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;
}
1 голос
/ 09 ноября 2011

Я просто хочу добавить одну вещь в предыдущий пост.Представьте, что вам нужна функция, которая принимает аргументы: (что-то вроде

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
0 голосов
/ 09 января 2018

Это мой способ определения функции в сборке, для этого не нужно иметь отдельный файл на ассемблере, и вам не нужно экранировать каждую новую строку. Вы можете просто скопировать содержимое файлов сборки в строковый литерал. Примечание: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * C ++ 11 * (Вы также пометили C ++). Это полезно, если вы хотите скомпилировать все в один .c - / .cpp -файл.

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
...