Je ne suis pas un expert dans ce domaine, mais je répondrai de mon mieux.
en commençant par le test d'octets REX:
if ((_rip[0] & 0xf0) == 0x40) /* REX byte present. */
{
unsigned char _rex = _rip[0] & 0x0f;
_is_64_bit = (_rex & 0x08) != 0;
_rip++;
}
L'octet REX est un préfixe d'instruction est utilisé en mode 64 bits. Si les 4 bits élevés du premier octet d'une instruction correspondent 0x40
, vous savez que vous avez un préfixe REX. Et si le bit 3 (le champ W) est défini sur 1, cela signifie que la taille de l'opérande est de 64 bits. _rip++
saute juste le dessus du préfixe.
if (_rip[0] == 0xf7)
F7
nous dit ceci est une instruction de division entière de quelque sorte.
{
bool _min_value_dividend = false;
unsigned char _modrm = _rip[1];
L'octet suivant est l'octet ModR/M qui donne habituellement les détails des opérandes, mais dans ce cas détermine également le type d'instruction de division.
if (((_modrm >> 3) & 7) == 7)
Le champ REG (bits 3 à 5) de l'octet ModR/M représente habituellement un registre, mais ici il est une extension de code d'opération de l'instruction. Si c'est 7, cela signifie que c'est une division signée.
{
if (_is_64_bit)
_min_value_dividend =
_gregs[REG_RAX] == (greg_t)0x8000000000000000UL;
else
_min_value_dividend =
(_gregs[REG_RAX] & 0xffffffff) == (greg_t)0x80000000UL;
}
0x80000000UL
et 0x8000000000000000UL
sont les plus petits nombres négatifs possibles en 32 bits et 64 bits respectivement. Si le registre eax (le dividende) correspond à cette valeur, cela signifie que vous avez le dividende minimum possible.
if (_min_value_dividend)
{
unsigned char _rm = _modrm & 7;
_gregs[REG_RDX] = 0; /* the remainder is zero */
Si vous avez le dividende minimum possible, cet ensemble le reste (EDX) à zéro, et laisse le dividende en EAX comme résultat.
switch (_modrm >> 6)
{
case 0: /* register indirect */
if (_rm == 5) /* 32-bit displacement */
_rip += 4;
if (_rm == 4) /* A SIB byte follows the ModR/M byte */
_rip += 1;
break;
case 1: /* register indirect + 8-bit displacement */
_rip += 1;
if (_rm == 4) /* A SIB byte follows the ModR/M byte */
_rip += 1;
break;
case 2: /* register indirect + 32-bit displacement */
_rip += 4;
if (_rm == 4) /* A SIB byte follows the ModR/M byte */
_rip += 1;
break;
case 3:
break;
}
_rip += 2;
_gregs[REG_RIP] = (greg_t)_rip;
return;
}
Le reste du code est juste inspectait l'octet ModR/M pour déterminer le nombre d'octets utilisés par l'opérande diviseur afin qu'il puisse faire avancer le pointeur d'instruction à l'instruction suivante. Fondamentalement, cela fait exactement ce qu'il dit dans les commentaires. Si le dividende est l'entier négatif de la plus grande magnitude possible, le résultat est égal au dividende et aucune exception ne se produit.
Comme pour _Jv_catch_segv
et _Jv_catch_segv
, ceux-ci sont définis dans segvpatch.cpp.
SIGNAL_HANDLER(catch_segv)
{
unblock_signal(SIGSEGV);
MAKE_THROW_FRAME(nullp);
handle_segv();
}
SIGNAL_HANDLER(catch_fpe)
{
unblock_signal(SIGFPE);
#ifdef HANDLE_DIVIDE_OVERFLOW
HANDLE_DIVIDE_OVERFLOW;
#else
MAKE_THROW_FRAME(arithexception);
#endif
handle_fpe();
}
La macro SIGNAL_HANDLER
est définie dans x86_64-signal.h et étend à quelque chose comme ceci:
static void _Jv_catch_segv (int, siginfo_t *, void *_p __attribute__ ((__unused__)))
Enfin, la macro RESTORE2
, qui est essentiellement appelé à partir RESTORE (restore_rt, __NR_rt_sigreturn)
, se déplie:
asm
(
".text\n"
".byte 0 # Yes, this really is necessary\n"
".align 16\n"
"__restore_rt:\n"
" movq $__NR_rt_sigreturn, %rax\n"
" syscall\n"
);
Cela crée un appel système de sigreturn qui est utilisée pour renvoyer à partir d'un gestionnaire de signaux. Ceci est transformé en fonction restore_rt
avec cette ligne:
void restore_rt (void) asm ("__restore_rt")
qui est défini comme le pointeur de fonction de rétablissement dans le code suivant:
act.k_sa_restorer = restore_rt;
qui est utilisé lors de l'initialisation des deux gestionnaires de signaux en INIT_SEGV
et INIT_FPE
.
Et je pense que couvre de toutes vos questions, mais s'il y a quelque chose qui est pas clair ou vous voulez que je développiez un aspect particulier, laissez-moi savoir dans les commentaires.
Merci beaucoup pour votre réponse. Je n'ai suivi la question: Selon cette page « http://man7.org/linux/man-pages/man2/sigaction.2.html » le sa_restorer est obsolète et ne doit pas être utilisé. Savez-vous quoi d'autre peut être utilisé à sa place? Ou cette fonctionnalité a-t-elle été complètement supprimée? Ou la différence principale pourrait-elle être dans le type de la structure 'k_sa_sigaction', c'est-à-dire la sigaction du noyau. Et ma dernière question - est ce fichier basé sur des connaissances en assembleur de base, ou devrais-je lire un peu sur la programmation du système d'exploitation avancé? – NindzAI
Vous devriez être en mesure de comprendre l'assemblage en lisant simplement la [Intel Instruction Set Reference] (http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html). La gestion du signal est plus compliquée. Je ne fais pas beaucoup de programmation Unix, donc c'est un peu hors de ma ligue, mais je pense que vous devez regarder la source du noyau pour vraiment comprendre ce qui se passe - il diffère d'une architecture à l'autre. Il semble sûr de déposer sa_restorer sur certaines plates-formes, mais x86-64 par exemple, on dirait qu'il est toujours nécessaire. –