Utiliser le pré-processeur `GCCs` comme assembleur

Il existe divers assembleurs de sources ouvertes tels que gas , nasm et yasm . Ils ont différentes pseudo-ops et syntaxes de macro . Pour de nombreux projets open source, l’assembleur est prétraité pour remplacer les constantes et les conditions de plate-forme.

Quelles limites gcc aurait-il à créer un assembleur en supposant que vous puissiez utiliser tous les atsortingbutes et les #pragmas , à l’exclusion des performances de traduction (compilation / assemblage en temps binary)?

Je ne parle pas d’ assemblage en ligne .

  #define MOV(RA,RB) (OXFEB10000UL | RA << 16 | RB) #define ADD(RA,RB) (OXFEB20000UL | RA << 16 | RB) #define RET (OXFEB7ABCDUL) unsigned long add4[] __attribute(section(".text")) = { ADD(R0,R1), ADD(R2,R3), MOV(R1,R2), ADD(R0,R1), RET() }; 

Je crois que l’utilisation de l’arithmétique de pointeur peut permettre la simulation de . et d’autres labels . C’est peut-être un problème XY ; J’essaie de comprendre pourquoi il y a tant d’assembleurs. Il semble que tout puisse être fait par le pré-processeur et que l’assembleur soit vraiment une préférence du programmeur; ou il y a une limitation technique qui me manque.

Je suppose que cela pourrait être lié à “quelque chose que vous pouvez faire avec un assembleur que vous ne pouvez pas faire avec un code shell “.

Edit: J’ai re-tagué cela du C au compilateur . Je m’intéresse aux détails techniques d’un assembleur. S’agit-il simplement d’une traduction 1-1 et de relocalisations émetsortingces (comme le fera le compilateur) ou y a-t-il plus? Je ne parle pas d’assembleur de code comme je l’ai expliqué ci-dessus. J’essaie de comprendre ce que font les assembleurs. Je ne crois pas qu’il existe un livre de dragon pour les assembleurs. Bien entendu, le pré-processeur ne peut pas créer un binary par lui-même et nécessite des machines supplémentaires; il ne traduit que le texte.

Quelles limites gcc aurait-il à créer un assembleur […]?

Beaucoup. Il y a une raison pour laquelle nous utilisons des assembleurs pour l’assemblage et des préprocesseurs C pour le prétraitement.

Premièrement, comme vous venez de le montrer vous-même, vous ne pouvez pas utiliser la syntaxe d’assembleur normale, que ce soit en style Intel ou AT & T. Vous devez utiliser ces parenthèses laides.

Deuxièmement, les directives __atsortingbute__ vous parlez n’ont rien à voir avec le pré-processeur, il ne les reconnaît même pas. Ce sont des astuces pour le compilateur, qui produira à son tour un code assembleur guidé par ces attrbutes (ou non).

Peut-être que c’est un problème XY

C’est à coup sûr.

J’essaie de comprendre pourquoi il y a tant d’assembleurs.

Pour la même raison, il existe différents types de langages de programmation, compilateurs, voitures et vêtements: un seul outil ne convient pas à tous les besoins. Les gens sont différents, ils font des choses différentes avec leur chaîne d’outils, ils trouvent l’un plus facile à utiliser que l’autre (j’utiliserais personnellement l’assembleur GNU s’il n’exigeait pas la syntaxe AT & T, que je ne peux tout simplement pas supporter), etc.

Je pense que XY Problem est une description fausse. La question est plus “Le concept A est nécessaire pour évaluer le concept B “.


Concept A: Qu’est-ce qu’un assembleur?

Voir: Assembleurs et chargeurs , de David Solomon . [des perles de sagesse, des anecdotes archaïques]

J’ai très vite découvert le manque de littérature dans ce domaine. Contrairement aux compilateurs, pour lesquels il existe une grande variété de littérature, très peu de choses ont été écrites sur les assembleurs et les chargeurs.

Un assembleur est composé de,

  • Une table de symboles pour faciliter la liaison à travers un format d’object.
  • Lexer et Parser pour convertir le texte en structure de données ou directement en code machine.
  • Fait 2 passes pour les appels les plus efficaces de twig et de sous-routine.
  • Une table d’opcode.

Un assembleur est généralement une traduction 1-1 . Cependant, il existe souvent plusieurs variantes de twigs et d’appels; généralement connu comme version longue et courte . Le code d’opération utilisé dépend de la distance à la destination; un compilateur à deux passes est nécessaire pour optimiser les twigs en aval. Fait allusion à par Harold


Concept B: Utiliser le pré-processeur ‘C’ en tant qu’assembleur.

Le mieux qu’un pré-processeur ‘C’ puisse émuler est un assembleur en une passe. Une grande classe de CPU / instructions peut être encodée comme ceci; bien que les macros puissent être encombrants. Il n’y aurait pas de listes ni de xréfs , mais la plupart des gens ne manqueraient pas ces fonctionnalités. En outre, la syntaxe serait étrange en raison de la limitation du pré-processeur. Il serait difficile de régler les adresses, car les étiquettes réutiliseraient la table des symboles “C” en utilisant des pointeurs ou une #define codée à la main pour le décalage de l’étiquette. Cela limite cette approche à tout sauf à un bloc de base .

Routines Grand Assembleur

Les routines d’assembleur volumineuses telles que les transformations YUV / RGB ou le décodage MP3 ont peu de chances d’être utilisées de cette manière.

Code multi-arch

Le code d’architecture multiple est assez commun. Par exemple, une puce wifi ARM peut avoir son code intégré dans un kernel Linux sous forme de microprogramme. Il est possible que cette technique soit utile ici. Cependant, utiliser des compilateurs / assembleurs distincts pour les différentes architectures, puis utiliser objcopy pour les incorporer, est beaucoup plus rationnel.

Code auto-modifiable

C’est probablement le plus utile. En fait, de nombreux outils, tels que les lieurs et les chargeurs, ont des fonctions de haut niveau qui corrigent le code lors de l’exécution. Il peut également être utilisé pour modifier de manière conditionnelle une routine au moment de l’exécution. les pointeurs de fonction sont presque aussi rapides et faciles à comprendre, sans parler des problèmes de cohérence du cache.

Voir aussi: Gold Blog , par Ian Lance Taylor . [bien qu’il utilise ]