2011-11-08 16 views
5

Chcę użyć naked function w moim programie C++ za pomocą g ++. Niestety g ++, w przeciwieństwie do VC++, nie obsługuje nagich funkcji, a jedynym sposobem zarządzania tym jest napisanie własnego kodu zespołu w osobnym pliku i połączenie z plikami C++. Próbowałem znaleźć dobry tutorial dla x86, aby połączyć zbiory i pliki C/C++, ale nie udało mi się znaleźć dobrego.Mieszanie plików C i Assembly

Uprzejmie proszę dać mi znać, jeśli znasz jakieś. Zauważ, że nie pytam o wbudowany zestaw, ale łączenie C i plików zespołu oraz sposoby zadeklarowania zewnętrznych zmiennych C w zespole i vice versa oprócz użycia ich w C lub zespole, a także sposoby łączenia plików C i ASm za pomocą Makefile .

+0

Spróbuj i ułóż to bardziej szczegółowo - jest raczej szeroki i niejasny w swojej formie - w przeciwnym razie prawdopodobnie zostanie zamknięty jako "nie jest to prawdziwe pytanie". Przypomnij sobie także wcześniejszą radę we wcześniejszych pytaniach o używanie 'gcc -S ...' do tworzenia szablonów asm itd. –

+0

gcc w tym samym stylu ma '__attribute __ ((naga))' ale nie dla x86 :( –

Odpowiedz

16

w C++ pliku:

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

int main() { 
    foo(); 
} 

w "gołym" pliku ASM dla 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 

kompilacji, montażu i łącza (z g ++ zamiast LD, ponieważ dużo łatwiej to zrobić w ten sposób dla C++) i uruchomić:

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

Oczywiście, jeśli chcesz przekazać argumenty do funkcji lub zwrócić wszystko y Musisz uszanować konwencje wzywające.

1

Chcę tylko dodać jedną rzecz do poprzedniego postu. Wyobraź chcesz funkcji, które akceptują argumenty: (coś jak

int add(int,int);

prototyp)

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

tak, z wyjątkiem nie używaj powolnej instrukcji 'enter'. http://agner.org/optimize/.Nie ma powodu, aby w ogóle budować tutaj ramkę stosu. Po prostu 'mov eax, [esp + 4]'/'add eax, [esp + 8]'/'ret'. Ponadto, nie przerywaj 'ebx'; jest to rejestr zachowujący połączenia we wszystkich zwykłych konwencjach wywoływania. Obalenie za niebezpieczny (i nieefektywny) przykład, i że nie pokazałeś, jak zbudować + połączyć to z NASM. –

5

Oto przykład trick do osiągnięcia "nagi funkcję" efekt.

#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

Nie potrzebujesz nawet funkcji fikcyjnej jako opakowania, ponieważ podstawowy zespół wbudowany może być umieszczony w zasięgu globalnym. –

0

To jest mój sposób zdefiniować funkcję w montażu, nie trzeba mieć oddzielny plik asemblera nie trzeba uciekać każdą linią. Możesz po prostu skopiować zawartość plików zespołu do łańcucha literałowego. Uwaga: The raw multiline string literal jest funkcją C++ 11 (również oznaczono C++). Jest to przydatne, jeśli chcesz skompilować wszystko w jednym pliku-.c -/.cpp.

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

Można użyć tylko podstawowej instrukcji złożenia bez dodatkowych parametrów w zasięgu globalnym. Korzystając z GCC lub Clang i procesora ręcznego, można używać [[gnu::naked]]/__attribute__((naked)).

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

Pierwszy sposób zawsze pozwala na zdefiniowanie nagich funkcji. Pomaga to również w tworzeniu bardziej przenośnego kodu.

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

Możesz użyć podstawowej instrukcji ASM w zasięgu globalnym w GCC/G ++. Nie można używać rozszerzonego złożenia wbudowanego w zasięgu globalnym. Podstawowa instrukcja składania jest zawsze niejawnie zmienna w dokumentacji. –

+0

Twój przykład x86 vs. x86-64 jest nieco głupi. 'i eax, 255' robi to, co chcesz na x86-32 i x86-64, i jest jeden bajt krótszy niż' i rax, 255'. Również 'movzx eax, al' jest nieco lepszy w obu, ponieważ jest krótszy. Jesteś pewien, że to się nawet złoży? Dyrektywa dotycząca gazu to ".globl" lub ".global", a nie "global". Użycie 'gcc -masm = intel' nadal używa dyrektyw GAS, a nie NASM. (Będzie się dobrze kompilował, ale nie montuje. Na "godbolt" używaj trybu "binarnego", aby upewnić się, że się on zbiera i kompiluje). Również ARM nie ma 'eax', ma' r0'. –

+0

Oczywiście, asm dla 'rand_byte' jest głupie tak. O wiele lepiej jest po prostu uczynić 'uint8_t rand_byte' słabym aliasem dla' rand', gdy konwencja wywołująca pozwala wąskim wartościom zwracanym mieć wysokie śmieci (jak na x86, ale nie ARM), i pozwolić wywołującemu wstawić zerowe rozszerzenie jeśli to potrzebuje . Niemożliwe jest wymyślanie przykładów dla wbudowanego asma, w którym nie byłoby lepiej po prostu https://gcc.gnu.org/wiki/DontUseInlineAsm, ale warto przynajmniej o tym wspomnieć. (Z wyjątkiem może uprzywilejowanych instrukcji, które nie mają wbudowanych/wewnętrznych wrapperów). –