Je programme le côté hôte d'un système d'accélérateur d'hôte. L'hôte fonctionne sur le PC sous Ubuntu Linux et communique avec le matériel intégré via une connexion USB. La communication est réalisée en copiant des fragments de mémoire vers et depuis la mémoire du matériel intégré.
Sur la mémoire du tableau, il y a une région de mémoire que j'utilise comme boîte aux lettres pour écrire et lire les données. La boîte aux lettres est définie comme une structure et j'utilise la même définition pour allouer une boîte aux lettres en miroir dans mon espace hôte.
Auparavant, j’utilisais cette technique avec succès et j’ai maintenant copié le projet Host Eclipse dans l’espace de travail de mon projet actuel et apporté les modifications de nom appropriées. La chose étrange est que lors de la construction du projet Host, le message suivant s'affiche:
Cible de construction: fft2d_Host
Invoquer: GCC C Linker
gcc -L/opt/adapteva/esdk/tools/Host/x86_64/lib -o "fft2d_Host" ./src/fft2d_Host.o -le_Host -lrt./src/fft2d_Host.o: Dans la fonction `main ':
fft2d_Host.c :(. text + 0x280): relocalisation tronquée pour tenir: R_X86_64_PC32 contre le symbole `Mailbox 'défini dans la section COMMON dans ./src/fft2d_Host.o
Qu'est-ce que cette erreur signifie et pourquoi elle ne s'appuie pas sur le projet actuel, alors qu'elle convient à l'ancien projet?
Vous essayez de lier votre projet de telle sorte que la cible d'un schéma d'adressage relatif soit plus éloignée que ne peut être prise en charge avec le décalage de 32 bits du mode d'adressage relatif choisi. Cela peut être dû au fait que le projet actuel est plus volumineux, qu'il lie les fichiers objet dans un ordre différent ou qu'il existe un schéma de mappage inutilement expansif.
Cette question est un exemple parfait de la raison pour laquelle il est souvent productif d'effectuer une recherche Web sur la partie générique d'un message d'erreur - vous trouvez des éléments tels que:
http://www.technovelty.org/code/c/relocation-truncated.html
Ce qui offre quelques suggestions curatives.
Exemple minimal générant l'erreur
main.S
: déplace un adresse dans %eax
(32 bits):
_start:
mov $_start, %eax
linker.ld
:
SECTIONS
{
/* This says where `.text` will go in the executable. */
. = 0x100000000;
.text :
{
*(*)
}
}
Compiler sur x86-64:
as -o main.o main.S
ld -o main.out -T linker.ld main.o
Résultat de ld
:
(.text+0x1): relocation truncated to fit: R_X86_64_32 against `.text'
Garde en tête que:
as
met tout sur le .text
si aucune autre section n'est spécifiéeld
utilise le .text
comme point d’entrée par défaut si ENTRY
. Ainsi _start
est le tout premier octet de .text
.Comment y remédier: utilisez cette linker.ld
à la place et soustrayez 1 du début:
SECTIONS
{
. = 0xFFFFFFFF;
.text :
{
*(*)
}
}
Remarques:
nous ne pouvons pas faire _start
global dans cet exemple avec .global _start
, sinon cela échoue toujours. Je pense que cela se produit car les symboles globaux ont des contraintes d’alignement (0xFFFFFFF0
travaux). TODO Où cela est-il documenté dans la norme ELF?
le .text
Le segment a également une contrainte d’alignement de p_align == 2M
. Mais notre éditeur de liens est assez intelligent pour placer le segment à 0xFFE00000
, remplissez de zéros jusqu’à 0xFFFFFFFF
Et mettre e_entry == 0xFFFFFFFF
. Cela fonctionne, mais génère un exécutable surdimensionné.
Testé sur Ubuntu 14.04 AMD64, Binutils 2.24.
Explication
Vous devez d’abord comprendre ce qu’est la relocalisation avec un exemple minimal: https://stackoverflow.com/a/30507725/895245
Ensuite, jetez un oeil à objdump -Sr main.o
:
0000000000000000 <_start>:
0: b8 00 00 00 00 mov $0x0,%eax
1: R_X86_64_32 .text
Si nous examinons la manière dont les instructions sont codées dans le manuel d’Intel, nous constatons que:
b8
dit qu'il s'agit d'un mov
à %eax
0
est une valeur immédiate à déplacer vers %eax
. La relocalisation le modifiera alors pour contenir l'adresse de _start
.Lors du passage à des registres 32 bits, l'immédiat doit aussi être 32 bits.
Mais ici, la relocalisation doit modifier ces 32 bits pour mettre l'adresse de _start
en eux après la liaison.
0x100000000
ne rentre pas dans 32 bits, mais 0xFFFFFFFF
Est-ce que. Donc l'erreur.
Cette erreur ne peut se produire que sur les relocalisations générant une troncature, par exemple. R_X86_64_32
(8 octets à 4 octets), mais jamais sur R_X86_64_64
.
Et il existe certains types de relocalisation qui requièrent l'extension sign au lieu d'une extension zéro, comme indiqué ici, par exemple. R_X86_64_32S
. Voir aussi: https://stackoverflow.com/a/33289761/895245
N'oubliez pas de traiter les messages d'erreur dans l'ordre. Dans mon cas, l'erreur au-dessus de celle-ci était une "référence indéfinie" et je l'ai visuellement ignorée à l'erreur plus intéressante "de relocalisation tronquée". En fait, mon problème était une ancienne bibliothèque qui provoquait le message "référence non définie". Une fois que j'ai résolu le problème, la "relocalisation tronquée" a également disparu.
J'ai rencontré ce problème lors de la création d'un programme nécessitant une énorme quantité d'espace de pile (plus de 2 Gio). La solution a été d’ajouter le drapeau -mcmodel=medium
, qui est pris en charge par les compilateurs GCC et Intel.
Souvent, cette erreur signifie que votre programme est trop volumineux et souvent trop volumineux, car il contient un ou plusieurs objets de données très volumineux. Par exemple,
char large_array[1ul << 31];
int other_global;
int main(void) { return other_global; }
produira une erreur "relocalisation tronquée pour tenir" sur x86-64/Linux, si elle est compilée dans le mode par défaut et sans optimisation. (Si vous activez l'optimisation, il pourrait, du moins théoriquement, déterminer que large_array
Est inutilisé et/ou que other_global
N'est jamais écrit et générer ainsi un code qui ne provoque pas le problème. .)
Ce qui se passe, c'est que, par défaut, GCC utilise son "modèle de petit code" sur cette architecture, dans laquelle tout le code du programme et les données allouées de manière statique doivent tenir dans les 2 Go les plus bas de l'espace d'adressage. (La limite supérieure précise est de l'ordre de 2 Go à 2 Mo, car les 2 Mo les plus bas de l'espace adresse d'un programme sont inutilisables en permanence. Si vous compilez une bibliothèque partagée ou un exécutable indépendant de la position, l'intégralité du code et des données doit tenir dans deux gigaoctets, mais ils ne sont plus cloués au bas de l'espace d'adressage.) large_array
consomme tout cet espace tout seul, de sorte que other_global
se voit attribuer une adresse supérieure à la limite et le code généré pour main
ne peut pas l’atteindre. Vous obtenez une erreur cryptique de la part de l'éditeur de liens, plutôt qu'une erreur utile du compilateur "large_array
Is too large", parce que dans des cas plus complexes, le compilateur ne peut pas savoir que other_global
Sera supprimé. de portée, de sorte qu'il n'essaye même pas pour les cas simples.
La plupart du temps, pour obtenir cette erreur, la bonne solution consiste à refactoriser votre programme afin qu'il n'ait pas besoin de gigantesques tableaux statiques et/ou de gigaoctets de code machine. Cependant, si vous devez vraiment les avoir pour une raison quelconque, vous pouvez utiliser le modèles de code "moyen" ou "grand" pour lever les limites, au prix d'une génération de code un peu moins efficace. Ces modèles de code sont spécifiques à x86-64; quelque chose de similaire existe pour la plupart des autres architectures, mais l'ensemble exact des "modèles" et les limites associées varieront. (Sur une architecture 32 bits, par exemple, vous pourriez avoir un "petit" modèle dans lequel la quantité totale de code et de données était limitée à quelque chose comme 224 octets)
Sur Cygwin -mcmodel=medium
est déjà la valeur par défaut et n’aide pas. Pour moi en ajoutant -Wl,--image-base -Wl,0x10000000
à l’éditeur de liens GCC a corrigé l’erreur.