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

course sur SIGFPE



Bonjour à tous.

Je ne peux que relancer mon sujet en essayant d'être plus clair.

(i) je cherche à récupérer depuis l'assembleur le signal SIGFPE, qui correspond aux erreurs numériques déclenchées par le coprocesseur mathématique, mais aussi par le processeur (instruction div ou idiv). (ii) en défrichant à l'aide de C (et de gcc -S), j'ai vu un comportement très spécial du préprocesseur de gcc: les symboles d'indices pour indexer le type tableau "greg_t" (défini dans <sys/ucontext.h>) ne sont pas visibles.

Pour le premier point, j'ai progressé en assembleur. Le nombre de "Memory access violation" que j'ai récupéré depuis des programmes écrits tant en C (en contournant le point (ii)) qu'en assembleur m'a convaincu que le pointeur sur un ucontext_t passé à mon handler était NULL ! D'où l'idée que j'étais directement dans le bon contexte. Du coup, il suffit de remplacer le 'ret' en fin de handler par un 'pop' suivi d'un 'jmp' pour obtenir le comportement que je souhaitais: traiter l'erreur en un point de chute précis de mon programme. Ce qui n'est faisable qu'en assembleur! C peut arriver à un résultat proche avec "setjmp / longjmp", mais pas tout à fait ce que je cherche.

Linux fonctionne donc pour sigaction très différemment de Solaris, au vu de l'exemple trouvé sur Internet. Du coup, pas besoin du troisième paramètre du handler, de type (de fait) *ucontext_t.

Toutefois il me reste un problème: une fois le handler utilisé, SIGFPE retrouve son comportement par défaut. Voici ce que j' obtiens avec mon programme envoyant en boucle à la procédure effectuant le travail les valeurs 0, 1, 2 et 3, le dividende étant 6:

SIGFPE récupéré
Valeur reçue sur eax: FFFFFFFF.
Valeur reçue sur eax: 00000006.
Valeur reçue sur eax: 00000003.
Valeur reçue sur eax: 00000002.

La valeur FFFFFFFF est celle mise en place par le traitement d'erreur. 6, 3 et 2 sont les résultats de division par 1, 2 et 3, ensuite 0 est de nouveau transmis, mais le message pour SIGFPE du système n'est même pas dans le stderr du programme, je présume que c'est le shell qui me fait le compte-rendu. Ce qui montre que le handler dans le programme a cessé d'être pris en compte. J'ai fait plusieurs essais en réutilisant "sigaction", je n'ai pas jusque là trouvé de solution.

Pour si quelqu'un est curieux, je joins le programme assembleur. N.B.: il est parfaitement autonome (à condition d'utiliser nasm). A propos, je ne veux pas de polémique sur l'usage de "int 80h", pour moi, quand je ne travaille pas avec C, je n'utilise pas C, point. La portabilité n'est pas un objectif quand on travaille en assembleur.



Pour le deuxième point, si j'ai (maladroitement semble-t-il) intitulé mon message précédent "usine à gaz des .h", c'est que je ne programme pas seulement en assembleur, mais aussi en Ada. A chaque fois que je veux appeler un service du système, c'est franchement la galère pour l'interfacer, je souhaiterais que le préprocesseur soit capable de fournir une meilleure documentation sur les données à utiliser (gcc -E élimine en les interprétant les #define, or ce sont eux qui définissent toutes les constantes). La programmation courante en C ne fait pas apparaître le problème, seulement voilà, "C est un langage que l'on peut qualifier de bas niveau" (citation tirée de "Le langage C, Norme ANSI, de Brian W. Kernighan et Denis M. Ritchie, deuxième édition, traduction française Masson / Prentice Hall 1997, p. 2, justifiée très simplement dans les lignes suivantes). Je ne l'utilise que pour des jeux d'essai ou de petites fonctions servant d'interfaces.

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.0
; Créé le 28 juin 2014
; Mis à jour le 30 juin 2014
;
; Auteur: Philippe Deleval
;
; Description:
;  version assembleur du programme de récupération de SIGFPE
;  version avec détournement du 'ret' du handler
;  Résultat des courses: le noyau enchaîne sur le handler de l'utilisateur
;  avec le contexte qu'il aurait s'il était appelé par 'call' depuis le point
;  ou 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.
;  Si ça marche sur la première captation de SIGFPE, je ne trouve pas le moyen
;  de relancer et réinstaller le 'handler' après usage!
;
; N.B.: le programme principal appelle la procédure "wrk" ave cles 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!)
;
	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.

; Values for the HOW argument to `sigprocmask'.
%define	SIG_BLOCK     0		; Block signals.
%define	SIG_UNBLOCK   1		; Unblock signals.
%define	SIG_SETMASK   2		; Set the set of blocked signals.

_start:	; programme fpe_asm
	call set_trap
	xor edx, edx
	mov [var], edx
	mov ecx, 10
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
	xor ebx, 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;
	;}
	mov eax, 6
	xor edx, edx
	div ebx
	; formellement résultat sur eax !
	leave
	ret
afferr:	mov edx, txtfpe.lg ; deux premiers paramètres pour sys_write
	mov ecx, txtfpe
	mov ebx, stderr
	mov eax, sys_write
	int 80h
	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]
	;  switch (signo) {
	mov eax, [ebp+8] ; premier paramètre
	cmp eax, SIGFPE
	jne .default
	;  case SIGFPE:
	;    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 *)
	;mov ebp, [edi+(20+4 * REG_EBP)]
	;mov esp, [edi+(20+4 * REG_ESP)]
	leave
	pop eax ; ret détourné
	jmp short afferr
	;    break;
	;  }
	;}
.default:
	;  default:
	;    fprintf(stdout, "default handler\n");
	mov edx, txtdef.lg
	mov ecx, txtdef
	mov ebx, stderr
	mov eax, sys_write
	int 80h
	leave
	ret
	
set_trap:	  ; établissement du "handler" pour SIGFPE
	; nettoyage de sa
	mov edi, sa
	mov ecx, 36 ; 4 * 36 = 144
	xor eax, eax
	rep stosd
	;mov dword [sa+4], 0ffff_ffffh
	;sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
	;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);
	mov edx, osa
	mov ecx, sa
	mov ebx, SIGFPE
	mov eax, sys_sigaction
	int 80h
	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é", 10
.lg	equ $-txtfpe
txtdef:	db "default handler", 10
.lg	equ $-txtdef

	SECTION .data
txtret:	db "Valeur reçue sur eax: "
.nb:	times 8 db '0'
	db '.', 10
.lg	equ $ - txtret
	SECTION .bss
var:	resd 1
sa:	resb 144
osa:	resb 144







Reply to: