Na co pozwala syscall w Linux execve – praktyczne przykłady

Wyobraź sobie, że chcesz uruchomić inny program z poziomu swojego procesu — na przykład uruchomić skrypt, program lub powłokę. W systemie Linux, aby to zrobić na najniższym poziomie, korzystamy z wywołania systemowego execve.

W tym artykule dowiesz się, czym jest syscall execve, jak działa i zobaczysz praktyczne przykłady jego użycia — zarówno z poziomu języka C, jak i asemblera.

Co to jest execve?

execve (ang. execute program) to wywołanie systemowe w Linuxie służące do uruchomienia nowego programu, zastępując obecny obraz procesu nowym programem.

  • Nie tworzy nowego procesu — zamienia kod, dane i stos bieżącego procesu.
  • Przyjmuje ścieżkę do programu, argumenty oraz zmienne środowiskowe.
  • Po wywołaniu execve obecny program przestaje istnieć — zastępuje go nowy.

Jak działa execve?

Wywołanie execve ma następującą sygnaturę:

int execve(const char *pathname, char *const argv[], char *const envp[]);
  • pathname — ścieżka do wykonywalnego pliku.
  • argv — tablica wskaźników do argumentów przekazywanych do programu.
  • envp — tablica wskaźników do zmiennych środowiskowych.

Jeśli wywołanie powiedzie się, nie wraca do programu wywołującego — proces jest zastąpiony nowym programem. Jeśli zwróci błąd, execve zwróci -1.

execve w asemblerze x86-64

Wywołania systemowe w Linux x86-64 wykonuje się instrukcją syscall. Numer execve to 59 (decimal).

Argumenty przekazujemy do rejestrów:

RejestrArgument
raxnumer syscall (59)
rdipathname
rsiargv
rdxenvp

Przykład minimalnego kodu w NASM, który uruchamia /bin/ls:

section .data
    path db '/bin/ls', 0
    argv dq path, 0
    envp dq 0

section .text
    global _start

_start:
    mov rax, 59        ; execve syscall number
    mov rdi, path      ; ścieżka programu
    mov rsi, argv      ; argumenty
    mov rdx, envp      ; środowisko
    syscall

    ; jeśli execve zawiedzie - zakończ program
    mov rax, 60        ; exit syscall
    xor rdi, rdi
    syscall

Kompilacja powyższego kodu na Linux

$ nasm -f elf64 -o run_ls_asm.o run_ls_asm.asm
$ ld -o run_ls_asm run_ls_asm.o

$ ./run_ls_asm
run_ls_asm run_ls_asm.asm run_ls_asm.o

Praktyczne zastosowania execve

1. Implementacja własnego exec w shellu

execve to fundament działania wielu powłok (shelli). Po wpisaniu polecenia:

ls -l /home

shell wykorzystuje fork() do utworzenia procesu potomnego, a w nim execve do uruchomienia /bin/ls z argumentami.

2. Uruchamianie skryptów lub programów z własnej aplikacji

Czasem aplikacja musi uruchomić zewnętrzny program, np. narzędzie do konwersji plików. execve pozwala na zastąpienie procesu tym programem, albo (częściej) wykorzystuje się fork() + execve(), by nie zamknąć aplikacji macierzystej.

3. Zastąpienie bieżącego procesu nowym programem

Jeśli w procesie chcesz całkowicie zmienić jego kod i dane (np. po zalogowaniu się użytkownika), wywołujesz execve, co zastępuje proces nowym obrazem.

execve vs system()

Funkcja system() w C uruchamia powłokę /bin/sh i wykonuje komendę. Jest wygodna, ale mniej elastyczna i bezpieczna niż bezpośrednie użycie execve.

Uwaga na błędy

  • Jeśli ścieżka jest błędna lub program nie ma uprawnień — execve zwróci błąd.
  • Pamiętaj o przekazaniu poprawnych tablic argumentów i środowiska.
  • execve nigdy nie wraca jeśli jest sukcesem!

Podsumowanie

  • execve to najniższy poziom uruchamiania programów w Linuxie.
  • Zastępuje obecny proces nowym obrazem programu.
  • Jest fundamentem działania powłok i procesów potomnych.
  • Można go używać bezpośrednio z asemblera, lub przez standardowe funkcje w C.

About the author

Autor "BIELI" to zapalony entuzjasta otwartego oprogramowania, który dzieli się swoją pasją na blogu poznajlinuxa.pl. Jego wpisy są skarbnicą wiedzy na temat Linuxa, programowania oraz najnowszych trendów w świecie technologii. Autor "BIELI" wierzy w siłę społeczności Open Source i zawsze stara się inspirować swoich czytelników do eksplorowania i eksperymentowania z kodem.