[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

SIGFPE de nouveau



Bonjour à tous

Je n'ai guère trouvé plus de documentation, mais "man sigreturn" m'a donné de nouvelles idées.

Le plus clair est dans le fichier joint (qui commence par 200 lignes de commentaires... c'est normal en assembleur), il me reste une question: quand je travaille en pur assembleur (fichier joint), le deuxième pointeur (*siginfo_t) passé au "handler" est NULL, alors que dès que je mélange C et assembleur, il pointe vers des données utilisables. Si quelqu'un est assez curieux pour m'aider, voir le fichier joint.

Cordialement

Philippe Deleval
; chapeau pour programmes ou modules écrits en assembleur
; inspiré du "header" suggéré par Jeff Duntemann ("Assembly Language Step
; by Step", troisième édition, Wiley 2009)
;
; fichier source: fpe_asm.asm
; fichier produit: fpe_asm
;
; version 1.2
; Créé le 28 juin 2014
; Mis à jour le 2 juillet 2014, nettoyage des scories de la version C trouvée
;	sur Internet
; Deuxième mise à jour (1.1) avec essai tiré d'une variante (tpe_sinf.asm). 
; Mis à jour le 7 juillet 2014, finalisation de la correction en pile et
;       concentration de la documentation dans le source présent
;
;
;
; Auteur: Philippe Deleval
;
; Description:
;  version assembleur du programme de récupération de SIGFPE
;
;   Résultat des courses: le noyau enchaîne sur le handler de l'utilisateur
; dans le contexte général (même pile) du code comprenant le point où
; le hardware a lancé 'int 5' (réaction aux erreurs de "Floating Point",
; mais aussi aux divisions par zéro ou débordements de 'div' ou 'idiv'), ce
; que le noyau convertit en SIGFPE. Mais le handler utilsateur fait retour
; vers les "arcanes" de Linux (faire "man sigreturn" pour voir).
;
;   Cette version finale marche en "corrigeant" la pile! L'adresse de retour
; (marquée par 'blob' dans le texte ci-dessous) est remplacée par l'adresse
; 'corerr' et, au retour de l'interruption et du signal, la main est donnée
; au correcteur d'erreur associée à la fonction 'wrk', comme je le souhaitais.
;
;   Deux autres variantes marchent avec cette idée de changer en plongeant dans
; la pile pour changer la valeur de retour empilée lors du déclenchement de
; l'interuption:
;   fpe_asm.asm, le texte présent, est entièrement en assembleur, autonome.
;   fpe_fctasm.c est écrit en C, mais interface un handler et appelle une
; fonction 'wrk' en assembleur (fichier fpe_mixte.asm).
;   A l'inverse, fpe_ca.asm est en assembleur, mais sigaction est appelé via
; une fonction écrite en C (fichier sethand.c).
;
;   L'offset dans la pile change pace que C fait je ne sais quelle magouille
; avec son type siginfo_t. Le pointeur "si" dans le programme ici contenu est
; NULL quand signal_handler est appelé (interface ci-dessous).
;     void signal_handler (int signo, siginfo_t *si, void *data)
; Dans les deux autres programmes, il n'est pas nul et le contenu pointé est
; dans la pile, pas loin de son "sommet". D'où les corrections fonctionnant:
;   Ici, dans fpe_asm : 'mov dword [ebp+68], corerr'
;   Dans fpe_mixte    : 'mov dword [ebp+0e0h], corerr'
;   Dans fpe_ca       : 'mov dword [ebp + 224], corerr'
;
;   En fait, la correction est la même dans les deux derniers cas. En effet,
; 0e0h=14*16=224.
;
;   Il semble donc que quelque chose dans gcc/Linux fait occuper 156 octets de
; plus dans la pile au gestionnaire de signal émis par le noyau sur int 5. Ce
; n'est pas l'objet de type siginfo_t qui est de taille __SI_MAX_SIZE=128 (voir
; dans la documentation ci-dessous). Il reste 28 autres octets à expliquer.
;
;
; N.B.: le programme principal appelle la procédure "wrk" avec les valeurs sur
; ebx 0, 1, 2, 3 cycliquement (effect des deux instructions 'inc eax' et
; 'and eax, 3' (i.e. 0000_0000_0000_0111 binaire).
; 
; 
; Commande d'assemblage: nasm -f elf fpe_asm.asm
; Commande d'édition de liens: ld -s -x -o fpe_asm fpe_asm.o (sauf si debug!)
;
;
;
;
; COMPLEMENTS DE DOCUMENTATION:
;
;
; tiré de <bits/sigset.h>:
;/* A `sigset_t' has a bit for each signal.  */
;
;# define _SIGSET_NWORDS	(1024 / (8 * sizeof (unsigned long int)))
;typedef struct
;  {
;    unsigned long int __val[_SIGSET_NWORDS];
;  } __sigset_t;
;
;
;
; tiré de <bits/sigaction.h> et simplifié en assumant __USE_POSIX199309 !
;
;/* Structure describing the action to be taken when a signal arrives.  */
;struct sigaction
;  {
;    /* Signal handler.  */
;    union
;      {
;	/* Used if SA_SIGINFO is not set.  */
;	__sighandler_t sa_handler;
;	/* Used if SA_SIGINFO is set.  */
;	void (*sa_sigaction) (int, siginfo_t *, void *);
;      }
;    __sigaction_handler;
;
;    /* Additional set of signals to be blocked.  */
;    __sigset_t sa_mask;
;
;    /* Special flags.  */
;    int sa_flags;
;
;    /* Restore handler.  */
;    void (*sa_restorer) (void);
;  };
;
;
;
; tiré de <bits/siginfo.h>
;# define __SI_MAX_SIZE     128
;# if __WORDSIZE == 64
;#  define __SI_PAD_SIZE     ((__SI_MAX_SIZE / sizeof (int)) - 4)
;# else
;#  define __SI_PAD_SIZE     ((__SI_MAX_SIZE / sizeof (int)) - 3)
;# endif
;
;typedef struct siginfo
;  {
;    int si_signo;		/* Signal number.  */
;    int si_errno;		/* If non-zero, an errno value associated with
;				   this signal, as defined in <errno.h>.  */
;    int si_code;		/* Signal code.  */
;
;    union
;      {
;	int _pad[__SI_PAD_SIZE];
;
;	 /* kill().  */
;	struct
;	  {
;	    __pid_t si_pid;	/* Sending process ID.  */
;	    __uid_t si_uid;	/* Real user ID of sending process.  */
;	  } _kill;
;
;	/* POSIX.1b timers.  */
;	struct
;	  {
;	    int si_tid;		/* Timer ID.  */
;	    int si_overrun;	/* Overrun count.  */
;	    sigval_t si_sigval;	/* Signal value.  */
;	  } _timer;
;
;	/* POSIX.1b signals.  */
;	struct
;	  {
;	    __pid_t si_pid;	/* Sending process ID.  */
;	    __uid_t si_uid;	/* Real user ID of sending process.  */
;	    sigval_t si_sigval;	/* Signal value.  */
;	  } _rt;
;
;	/* SIGCHLD.  */
;	struct
;	  {
;	    __pid_t si_pid;	/* Which child.  */
;	    __uid_t si_uid;	/* Real user ID of sending process.  */
;	    int si_status;	/* Exit value or signal.  */
;	    __clock_t si_utime;
;	    __clock_t si_stime;
;	  } _sigchld;
;
;	/* SIGILL, SIGFPE, SIGSEGV, SIGBUS.  */
;	struct
;	  {
;	    void *si_addr;	/* Faulting insn/memory ref.  */
;	  } _sigfault;
;
;	/* SIGPOLL.  */
;	struct
;	  {
;	    long int si_band;	/* Band event for SIGPOLL.  */
;	    int si_fd;
;	  } _sigpoll;
;      } _sifields;
;  } siginfo_t;
;
;....
;/* Values for `si_code'.  Positive values are reserved for kernel-generated
;   signals.  */
;....
;/* `si_code' values for SIGFPE signal.  */
;enum
;{
;  FPE_INTDIV = 1,		/* Integer divide by zero.  */
;# define FPE_INTDIV	FPE_INTDIV
;  FPE_INTOVF,			/* Integer overflow.  */
;# define FPE_INTOVF	FPE_INTOVF
;  FPE_FLTDIV,			/* Floating point divide by zero.  */
;# define FPE_FLTDIV	FPE_FLTDIV
;  FPE_FLTOVF,			/* Floating point overflow.  */
;# define FPE_FLTOVF	FPE_FLTOVF
;  FPE_FLTUND,			/* Floating point underflow.  */
;# define FPE_FLTUND	FPE_FLTUND
;  FPE_FLTRES,			/* Floating point inexact result.  */
;# define FPE_FLTRES	FPE_FLTRES
;  FPE_FLTINV,			/* Floating point invalid operation.  */
;# define FPE_FLTINV	FPE_FLTINV
;  FPE_FLTSUB			/* Subscript out of range.  */
;# define FPE_FLTSUB	FPE_FLTSUB};
;
;
;

	BITS 32
	GLOBAL _start

	SECTION .text

%idefine sys_exit	1
%idefine sys_read	3
%idefine sys_write	4
%idefine sys_sigaction	67
%idefine stdin		0
%idefine stdout		1
%idefine stderr		2

%define SIGFPE	8 ; code de SIGFPE d'après fpe_v0.s

; valeurs tirées de <bits/sigaction.h> 
; Bits in `sa_flags'.
%define	SA_NOCLDSTOP  1		; Don't send SIGCHLD when children stop.
%define SA_NOCLDWAIT  2		; Don't create zombie on child death.
%define SA_SIGINFO    4		; Invoke signal-catching function with
				;   three arguments instead of one.
%define SA_ONSTACK   0x08000000 ; Use signal stack by using `sa_restorer'.
%define SA_RESTART   0x10000000 ; Restart syscall on signal return.
%define SA_NODEFER   0x40000000 ; Don't automatically block the signal when
				;   its handler is being executed.
%define SA_RESETHAND 0x80000000 ; Reset to SIG_DFL on entry to handler.
%define SA_INTERRUPT 0x20000000 ; Historical no-op.

_start:	; programme fpe_asm
	; nettoyage de (sigaction) [sa]
	cld
	mov edi, sa
	mov ecx, 35 ; 4 * 35 = 140
	xor eax, eax
	rep stosd
	;mov dword [sa+4], 0ffff_ffffh ; [sa+4] = sa.sa_mask[0]
	;sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
	;sa.sa_flags = [sa+132]
	;[sa+132] car [sa] adresse ou entier (4 octets), et [sa+4] tableau de
	; 1024 / (8 * 4) = 32 'long integer' (qui sont 32 bits sur système
	;32 bits), donc 128 octets (type __sigset_t donné ci-dessus, sous le
	;chapeau).
	;mov dword [sa+132], 402653188 ; tiré de fpe_v0.s
	mov dword [sa+132], SA_SIGINFO
	;
	;sa.sa_sigaction = signal_handler;
	mov dword [sa], signal_handler ; tiré de fpe_v0.s
	; sigaction(SIGFPE, &sa, &osa);
	xor edx, edx ; NULL comme troisième paramètre &osa
	mov ecx, sa
	mov ebx, SIGFPE
	mov eax, sys_sigaction
	int 80h
	test eax, eax
	jnz errst
	;xor edx, edx
	mov edx, 1
	mov [var], edx
	mov ecx, 1024
bcpr:	push ecx
	mov eax, [var]
	mov ebx, eax
	inc eax
	and eax, 3
	mov [var], eax
	call wrk
	mov edi, txtret.nb
	call nbtohex
	mov edx, txtret.lg
	mov ecx, txtret
	mov ebx, stdout
	mov eax, sys_write
	int 80h
	pop ecx
.tstb:	loop bcpr
	mov edx, bye.lg
	mov ecx, bye
	mov ebx, stdout
	mov eax, sys_write
	int 80h
	xor ebx, ebx
	mov eax, sys_exit
	int 80h
errst:	; erreur dans sigaction
	mov edi, terrst.nb
	call nbtohex
	mov edx, terrst.lg
	mov ecx, terrst
	mov ebx, stderr
	mov eax, sys_write
	int 80h
	xor ebx, ebx
	dec ebx
	mov eax, sys_exit
	int 80h

wrk:	; prend ebx en paramètre
	; renvoie 1 ! mais si ebx=0, traitement d'erreur
	push ebp
	mov ebp, esp
	;
	;  b /= 0x0;
	;
	;  return b;
	;}
	;call vframe ; vframe sauvegarde ebx
	mov eax, 6
	xor edx, edx
blob:	div ebx
	; résultat sur eax
	leave
	ret
corerr:	mov edx, thandl.lg
	mov ecx, thandl
	mov ebx, stdout
	mov eax, sys_write
	int 80h
	;call vframe
	mov eax, 0ffff_ffffh
	leave
	ret


;void signal_handler (int signo, siginfo_t *si, void *data)
signal_handler:	push ebp
	mov ebp, esp
	;  /* ucontext_t *uc;
	;     uc = (ucontext_t *) data; */
	;; uc sur [ebp+16]
	; fprintf(stdout, "Caught FPE\n");
	; reporté en fonction appeleuse.
	;; /*uc->uc_mcontext.gregs[REG_PC]=uc->uc_mcontext.gregs[REG_nPC];*/
	;; corrigé en uc->uc_mcontext.gregs[REG_EBX]=1;
	;;mov edi, [ebp+16] ;troisième paramètre (ucontext_t *)
	;;test edi, edi ;[ebp+16] semble bien null = 0 !
	;mov ebp, [edi+(20+4 * REG_EBP)] ?
	;mov esp, [edi+(20+4 * REG_ESP)] ?
	;mov dword [edi+off_ebx], 1
	;mov dword [edi+off_edx], 1
	;mov dword [edi+off_ecx], 1
	;mov dword [edi+off_eax], 1
	; sortie du handler, redirection sur prototype de exception handler
	mov edx, txtfpe.lg ; deux premiers paramètres pour sys_write
	mov ecx, txtfpe
	mov ebx, stdout
	mov eax, sys_write
	int 80h
	;mov esi, ebp
	;mov ecx, 20
	;.bctra:	mov eax, [esi]
	;cmp eax, blob
	;je .sbtra
	;add esi, 4
	;loop .bctra
	;mov edx, tntr.lg
	;mov ecx, tntr
	;mov ebx, stderr
	;mov eax, sys_write
	;int 80h
	;mov ebx, 0ffff_ffffh
	;mov eax, sys_exit
	;int 80h
	;.sbtra:
	mov dword [ebp+68], corerr
	leave
	ret

;vframe:
	;;affichage du "contexte" (stack frame) de la procédure appelante
	;push ebp
	;mov ebp, esp
	;push ebx ; sauvegarde de ebx pour cause d'appel par wrk
	;mov eax, esp
	;add eax, 12 ; adresse retour plus empilage de ebp et place de ebx
	;mov byte [txtsf.info], 's'
	;mov edi, txtsf.nb
	;call nbtohex
	;mov edx, txtsf.lg
	;mov ecx, txtsf
	;mov ebx, stdout
	;mov eax, sys_write
	;int 80h
	;mov eax, [ebp] ; valeur empilée de ebp
	;mov byte [txtsf.info], 'b'
	;mov edi, txtsf.nb
	;call nbtohex
	;mov edx, txtsf.lg
	;mov ecx, txtsf
	;mov ebx, stdout
	;mov eax, sys_write
	;int 80h
	;pop ebx
	;leave
	;ret

nbtohex:
	; attend en paramètre sur eax un nombre (32 bits non signé)
	;et l'adresse d'un buffer sur edi
	;;  affiche en quatre chiffres hexadécimaux de [edi] à [edi+3]
	mov edx, eax ; nombre
	; edi pointeur buffer (destination de la conversion)
	mov ecx, 8 ; nombre de chiffres
	add edi, ecx ; déplace le pointeur au dernier chiffre demandé
	dec edi ; correction!
	std
.boucle	mov eax, edx ; extraction du chiffre
	and eax, 000FH
	shr edx, 4 ; division par 16
	cmp eax, 0ah ; traitement différent si chiffre entre 0 et 9 ou A et F
	jb .mdix
	add eax, 37H
	jmp short .ecrire
.mdix	add eax, 30H
.ecrire	stosb ; utilise et met à jour le pointeur
	loop .boucle ; sortie si compteur ecx épuisé
	ret


	SECTION .rodata
txtfpe:	db "SIGFPE récupéré... "
.lg	equ $-txtfpe
tntr:	db "Retour non trouvé!", 10
.lg	equ $ - tntr
bye:	db "Tout s'est bien passé.", 10
.lg	equ $ - bye

	SECTION .data
txtret:	db "Valeur reçue sur eax: "
.nb:	times 8 db '0'
	db '.', 10
.lg	equ $ - txtret
;txtsf:	db "Contenu du registre e"
;.info:	db ' ', "p:"
;.nb:	times 8 db '0'
;	db '.', 10
;.lg	equ $ - txtsf
thandl:	db "Passage par corerr, traite-exception de wrk", 10
.lg	equ $ - thandl
terrst:	db "Code retour de sigaction: "
.nb:	times 8 db '0'
	db '.', 10
.lg	equ $ - terrst

	SECTION .bss
var:	resd 1
sa:	resb 144







Reply to: