[LINUX] J'ai essayé Hello World avec un langage OS + C 64 bits sans utiliser de bibliothèque

Cet article est écrit comme l'article du 8ème jour du Calendrier de l'Avent d'OthloTech 2017.

J'en suis responsable pendant deux jours consécutifs depuis hier. Hier, c'était sur le mobile, mais aujourd'hui j'aimerais changer et aborder le noyau en langage C. Plus précisément, Hello World sera exécuté en utilisant uniquement les appels système définis dans le système d'exploitation.

__ Public cible __

** Contenu de l'article **

Environnement d'exploitation

Le premier Hello World

Écrivons Hello World, qui semble être le langage C le plus simple. C'est quelque chose que beaucoup de gens ont écrit, comme étudier à l'école.

Hello.c


#include<stdio.h>

int main(){
  printf("Hello World!\n");
  return 0;
}

Je l'ai écrit en utilisant le format d'impression. Vous pouvez facilement l'afficher en spécifiant simplement une chaîne de caractères. Cependant, cette fonction appelée printf ne fonctionnera que si stdio.h est inclus. C'est un soi-disant magique. L'opération à l'intérieur n'est pas bien comprise par cela seul.

Compilons et voyons à quoi ressemble l'exécutable. Comme il est débogué avec gdb, j'ai ajouté l'option -g, et comme le lien dynamique est paresseux, j'ai ajouté -static.

$ gcc Hello.c -static -g
$ gdb -q a.out
(gdb) break main
(gdb) layout asm
(gdb) run
(gdb) si
(gdb) si
     ...

J'ai essayé de marcher comme ça, mais ça ne se termine pas facilement. ~~ Je ne suis motivé qu'à la fin ~~ Je ne sais pas comment cela fonctionne, alors j'ai frappé strace, une commande qui énumère les appels système.

$ strace ./a.out
                                                 ~/hello
execve("./a.out", ["./a.out"], 0x7ffdd8a3ad60 /* 43 vars */) = 0
brk(NULL)                               = 0x1182000
brk(0x11831c0)                          = 0x11831c0
arch_prctl(ARCH_SET_FS, 0x1182880)      = 0
uname({sysname="Linux", nodename="Juju-62q", ...}) = 0
readlink("/proc/self/exe", "/home/(username)/hello/a.out", 4096) = 23
brk(0x11a41c0)                          = 0x11a41c0
brk(0x11a5000)                          = 0x11a5000
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
write(1, "Hello World\n", 12Hello World
)           = 12
exit_group(0)                           = ?
+++ exited with 0 +++

Il semble que d'une manière ou d'une autre, la mémoire est sécurisée et que diverses choses sont faites. Cependant, je pense que l'écriture peut afficher une chaîne de caractères. Ensuite, je vais l'utiliser pour générer une chaîne de caractères.

Essayez d'utiliser la fonction d'écriture en langage C

Lorsque j'ai recherché la fonction d'écriture, elle a été définie comme suit. (/usr/include/unistd.h) extern ssize_t write (int __fd, const void *__buf, size_t __n) __wur; fd est un descripteur de fichier. Cette fois, ce sera 1 car il est émis vers la sortie standard. buf est le contenu de sortie et n est le nombre de caractères. Cela semble gênant que printf, mais il semble que l'inconvénient ne soit pas pratique, donc il y a plus de choses qui ne sont pas utilisées () Incluons ceci.

Write.c


#include<unistd.h>

int main(){
  const void *string = "Hello Write!\n";
  write(1, string, 13);
  return 0;
}

J'ai essayé d'afficher une chaîne de caractères dans la sortie standard. Faisons en sorte que cela fonctionne de la même manière.

$ gcc Write.c -static -g
$ gdb -q a.out
(gdb) break main
(gdb) layout asm
(gdb) run
(gdb) si
(gdb) si
     ...

Cette fois, je suis arrivé à l'affichage des caractères relativement rapidement. Apparemment, il est affiché lors de l'appel de syscall. Mais je n'aime pas #include <unistd.h>. Si une chaîne de caractères est affichée par syscall, il semble que vous deviez la définir et appeler syscall, j'ai donc étudié comment utiliser syscall. Apparemment, des registres utilisés par le processeur 64 bits

rax = 1 (en spécifiant qu'il s'agit d'un appel système d'écriture) rdi = 1 (descripteur de fichier, c'est-à-dire 1 pour la sortie standard) rsi = (première adresse de la chaîne de caractères) (spécifier la chaîne de caractères d'affichage) rdx = (longueur de la chaîne) (nombre de caractères)

Il semble que la chaîne de caractères s'affiche lorsque syscall est appelé en tant que. Ensuite, appelons syscall directement depuis l'assembleur. Ensuite, vous pouvez dire au revoir à unistd.h détesté.

Je me réfère à ce qui suit http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/

Cliquez ici pour les registres CPU https://software.intel.com/en-us/articles/introduction-to-x64-assembly

Appelez syscall avec l'assembleur pour voir

Lors d'un appel en langage C, il semble que les spécifications ci-dessus ne peuvent être satisfaites que si au moins la chaîne de caractères et la longueur des caractères sont spécifiées, je voudrais donc spécifier l'argument. Lors de l'utilisation de l'argument avec un assembleur 64 bits, il semble que cela se passe comme suit.

rdi premier argument rsi deuxième argument rdi 3ème argument rcx 4ème argument r8 5ème argument r9 6ème argument

référence http://p.booklog.jp/book/34047/page/613687

Puisqu'il y a deux arguments cette fois, nous utiliserons rdi et rsi. Ensuite, stockez la première adresse de la chaîne de caractères dans rsi, le nombre de caractères dans rdx et 1 dans rax et rdi. Sur la base de ce qui précède, j'ai écrit un assembleur pour nasm. Le nom de la fonction est bonjour.

syscall.asm


bits 64

global hello

hello:
  mov rdx, rsi
  mov rsi, rdi
  mov rax, 1
  mov rdi, 1
  syscall
  ret

J'ai écrit un programme en langage C en utilisant les fonctions créées par l'assembleur.

main.c


void hello(char *string, int len);

int main (){
  char *string = "Hello Asm!\n";
  hello(string, 11);
  return 0;
}

Enfin l'inclusion est partie !!! En langage C, hello prototype est déclaré et la fonction est exécutée. Compilons-le. Cette fois, un fichier objet est généré et lié pour connecter plusieurs fichiers.

$ nasm -f elf64 -o syscall.o syscall.asm
$ gcc -c main.c
$ gcc main.o syscall.o
$ ./a.out
Hello Asm!

J'ai pu sortir la chaîne de caractères que je veux sortir en toute sécurité! Dans nasm, -f elf64 est spécifié pour afficher le fichier objet pour 64 bits. Hello World (provisoire) a été créé uniquement par des appels système depuis le système d'exploitation sans utiliser la bibliothèque en toute sécurité! Bien sûr, c'est un début rudimentaire, mais j'ai l'impression d'avoir appris un peu comment utiliser le système d'exploitation.

Postscript: à propos de la routine de démarrage

Une routine de démarrage qui traite les principaux arguments lors de l'appel d'une fonction et retourne à la fin est appelée à partir de la bibliothèque. J'ai reçu un commentaire.

Pour le moment, je posterai le programme qui n'utilise pas la routine de démarrage que j'ai reçue. Je le mettrai à jour à nouveau si je peux le mettre moi-même.

$ cat -n main.c
     1  void hello(const char*, int);
     2  void exit(int) __attribute__((noreturn));
     3
     4  int main(void){
     5    const char* string = "Hello Asm!\n";
     6    hello(string, __builtin_strlen(string));
     7    exit(0);
     8  }
$ cat -n syscall.asm
     1  bits 64
     2
     3  global hello
     4
     5  hello:
     6    mov rdx, rsi
     7    mov esi, edi
     8    mov eax, 1
     9    mov edi, 1
    10    syscall
    11    ret
    12
    13  global exit
    14
    15  exit:
    16    mov esi, edi
    17    mov eax, 60
    18    syscall
$ cat -n makefile
     1  target:
     2          nasm -f elf64 -o syscall.o syscall.asm
     3          gcc -O2 -Wall -Wextra main.c syscall.o -nostdlib -static -Wl,-Map=main.map -Wl,-emain
$ make
nasm -f elf64 -o syscall.o syscall.asm
gcc -O2 -Wall -Wextra main.c syscall.o -nostdlib -static -Wl,-Map=main.map -Wl,-emain
$ ls -l a.out
-rwxrwxrwx 1 user user 1504 Dec  9 00:00 a.out
$ ./a.out
Hello Asm!
$

Résumé

Comment était-ce? ~~ J'espère que vous comprenez à quel point le langage C est haut de gamme. ~~ Je serais très heureux si vous pouviez appeler cet article et réaliser le plaisir et la profondeur des appels système. Il y a peu de gens dans OthloTech qui font de la couche basse, donc j'espère personnellement que cela augmentera à l'avenir lol Alors tout le monde, bonne vie!

Recommended Posts

J'ai essayé Hello World avec un langage OS + C 64 bits sans utiliser de bibliothèque
J'ai essayé d'utiliser la bibliothèque Python de Ruby avec PyCall
J'ai essayé d'utiliser la bibliothèque de programmation fonctionnelle toolz
J'ai essayé d'extraire des expressions uniques avec la bibliothèque de traitement du langage naturel GiNZA
J'ai essayé d'identifier la langue en utilisant CNN + Melspectogram
J'ai essayé la bibliothèque changefinder!
J'ai essayé d'illustrer le temps et le temps du langage C
J'ai essayé d'utiliser l'API checkio
Un débutant en Python a essayé Hello World en 30 secondes en utilisant le microframework Flask
J'ai essayé d'utiliser Amazon SQS avec django-celery
J'ai essayé d'utiliser du sélénium avec du chrome sans tête
J'ai essayé de comprendre attentivement la fonction d'apprentissage dans le réseau de neurones sans utiliser la bibliothèque d'apprentissage automatique (deuxième moitié)
J'ai essayé un langage fonctionnel avec Python
J'ai essayé de jouer avec l'image avec Pillow
J'ai essayé de visualiser le modèle avec la bibliothèque d'apprentissage automatique low-code "PyCaret"
J'ai essayé le traitement du langage naturel avec des transformateurs.
J'ai essayé d'utiliser l'API BigQuery Storage
J'ai essayé de comprendre attentivement la fonction d'apprentissage dans le réseau de neurones sans utiliser la bibliothèque d'apprentissage automatique (première moitié)
[Python] Deep Learning: J'ai essayé d'implémenter Deep Learning (DBN, SDA) sans utiliser de bibliothèque.
J'ai essayé d'utiliser la bibliothèque Python "pykakasi" qui peut convertir des kanji en romaji.
J'ai mesuré le temps pendant lequel j'ai installé le module dépendant du langage C avec alpin
J'ai essayé de "lisser" l'image avec Python + OpenCV
J'ai recherché dans la bibliothèque l'utilisation de l'API Gracenote
vprof - J'ai essayé d'utiliser le profileur pour Python
Bonjour tout le monde avec toutes les fonctionnalités du langage Go
J'ai essayé de "différencier" l'image avec Python + OpenCV
J'ai essayé de sauvegarder les données avec discorde
J'ai essayé d'utiliser PyCaret à la vitesse la plus rapide
J'ai essayé d'utiliser l'API Google Cloud Vision
Dites bonjour au monde avec Python avec IntelliJ
J'ai essayé d'utiliser mecab avec python2.7, ruby2.3, php7
J'ai essayé de "binariser" l'image avec Python + OpenCV
J'ai essayé d'utiliser Pythonect, un langage de programmation de flux de données.
J'ai essayé d'utiliser le module Datetime de Python
J'ai essayé la fonction SLAM de Pepper NAOqi OS 2.5.5
J'ai essayé d'utiliser le filtre d'image d'OpenCV
J'ai essayé DBM avec Pylearn 2 en utilisant des données artificielles
J'ai essayé d'utiliser la base de données (sqlite3) avec kivy
J'ai essayé de jouer avec la calculatrice avec tkinter
[MQTT] J'ai essayé de parler avec un appareil utilisant AWS IoT Core et Soracom Beam.
[Test de décorrélation] J'ai essayé d'éteindre la ligne de démarcation avec ou sans rejet
Appelez votre propre bibliothèque de langage C avec Go en utilisant cgo
J'ai essayé d'utiliser google test et CMake en C
J'ai essayé d'exécuter BERT avec Sakura VPS (sans GPU)
J'ai essayé de toucher un fichier CSV avec Python
J'ai essayé de résoudre Soma Cube avec python
FizzBuzz avec des expressions régulières etc. sans utiliser l'opérateur '%'
[Linux] J'ai essayé d'utiliser le logiciel de statistiques génétiques PLINK
J'ai essayé de regrouper les données ECG en utilisant la méthode K-Shape
J'ai essayé d'approcher la fonction sin en utilisant le chainer
J'ai essayé d'utiliser l'API de Sakenowa Data Project
J'ai essayé de résoudre le problème avec Python Vol.1
J'ai essayé de compléter le graphe de connaissances en utilisant OpenKE
J'ai essayé de frapper l'API avec le client python d'echonest
J'ai essayé de compresser l'image en utilisant l'apprentissage automatique
J'ai essayé d'utiliser la méthode Seaborn fréquemment utilisée avec le moins d'arguments possible [pour les débutants]