Please refer to Apue 7.10 For more information on how to use setjmp & longjmp.
This article explains the following points of knowledge:
1, Simple introduction of x86_64 register
2, setjmp & longjmp is how to achieve.
3, why can return from setjmp place several times.
4, when returning from setjmp, those data is invalid, how to avoid.
This article does not draw a function call stack frame diagram, if the assembly is not very familiar with, it is best to look at the side of the painting ^_^, will be related to the times.
The following is an analysis by disassembling the code below
#include <setjmp.h>
#include <stdio.h>
jmp_buf my_jum_buf;
void fun_fun()
{
printf("Enter fun_fun ...\n");
longjmp(my_jum_buf, 8);//如下代码不会被执行
printf("fun_fun::can‘t see");
}
void fun()
{
fun_fun();
}
int main()
{
int ret;
if(ret = setjmp(my_jum_buf))
{
printf("Main: return after calling longjmp, ret = %d.\n", ret);
}
else
{
printf("Main: first time return from setjmp, ret = %d\n", ret);
fun();
}
return 0;
}
output:
[email protected]:~/vm_disk_dpdk/study/apue# ./a.out
Main: first time return from setjmp, ret = 0
Enter fun_fun ...
Main: return after calling longjmp, ret = 8.
1, Simple introduction of x86_64 register
X86-64 General Register with respect to X86_32, the new addition%R8 to%r15, the original x86_32 of 8 (the original 32-bit register in the name of E to R), a total of 16 registers. The X86-64 has 16 64-bit registers, respectively:%rax,%rbx,%rcx,%rdx,%esi,%edi,%rbp,%rsp,%r8,%r9,%r10,%r11,%r12,%r13,%r14,%r15.
which
%rax is used as a function return value.
%RSP stack pointer register, pointing to the top of the stack
%RDI,%RSI,%RDX,%RCX,%R8,%R9 is used as a function parameter, corresponding to the 1th parameter, the 2nd parameter ...
%RBX,%RBP,%R12,%R13,%14,%15 is used as a data store, followed by the callee usage rule, which is to be backed up before the child function is used in case he is modified
%r10,%r11 is used as a data store, following the caller's rules of use, simply by saving the original value before use.
Supplement: (https://msdn.microsoft.com/zh-cn/library/6t169e9c.aspx):
Registers RAX, RCX, RDX, R8, R9, R10, R11 are considered volatile and must be considered destroyed when the function is called (unless the analysis is considered secure through full program optimization).
Registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered non-volatile and must be saved and restored by the function that uses them.
This is the x86_64 register using the Convention, if you want to return from the child function correctly, the child function only needs to save and restore nonvolatile registers can be, RDI, RSI is used as parameters.
to ensure the execution of longjmp, you can continue to execute correctly from the setjmp return, only the following two items are correct:
1, program execution flow is correct: can restore the value of the PC to call the setjmp instruction the next command address can be. When calling setjmp, the CPU hardware automatically presses the value of the PC (EIP) into the stack by backing up the data in the stack.
2, the data register back to the original: for the register as a data store, only focus on non-volatile registers, that is, the sub-function using the registers to be backed up (RBX, RBP, RSP, R12, R13, R14 and R15), back up in the setjmp.
that is, you only need to back up in setjmp: RBX, RBP, RSP, R12, R13, R14, R15, and PC.
2, setjmp & longjmp Realization--Data structure JMP_BUF:
The preprocessed file is obtained with the following command to obtain the appropriate structure:
[Email protected]:~/vm_disk_dpdk/study/apue# gcc-e setjmp.c > Setjmp.i
typedef long INT __jmp_buf[8];///8 registers (RBX, RBP, RSP, R12, R13, R14, R15, and PC) are stored in this variable,
typedef int __sig_atomic_t;
typedef struct
{
unsigned long int __val[(1024x768/(8 * sizeof (unsigned long int)))];
} __sigset_t;
struct __JMP_BUF_TAG
{
__jmp_buf __jmpbuf;
int __mask_was_saved;
__sigset_t __saved_mask;
};
typedef struct __JMP_BUF_TAG jmp_buf[1];
3, setjmp & longjmp Realization--setjmp:
Disassembly Analysis:
(GDB) Disassemble main
Dump of assembler code for function main:
0x000000000040063a <+0>: Push%RBP
0x000000000040063b <+1>: mov%rsp,%rbp
= = 0x000000000040063e <+4>: Sub $0X10,%RSP//esp at this time is the value before the call to setjmp, so setjmp to back up this E Sp
0x0000000000400642 <+8>: mov $0x601080,%edi//0x601080 is the address of the global variable My_jum_buf, deposited in the EDI register.
0x0000000000400647 <+13>: callq 0x4004f0 <[email protected]>//call directive implies push pc,
0X000000000040064C <+18>: mov%eax,-0x4 (%RBP)
0x000000000040064f <+21>: Cmpl $0x0,-0x4 (%RBP)
0x0000000000400653 <+25>: je 0x40066b <main+49>
.............................................
(GDB) Disassemble _setjmp
Dump of assembler code for function _setjmp:
0x00007ffff7a4bb20 <+0>: Xor%esi,%esi
0x00007ffff7a4bb22 <+2>: jmpq 0x7ffff7a4ba80 <__sigsetjmp>//jmp Instruction No stack operation
End of assembler dump.
(GDB) Disassemble __sigsetjmp
Dump of assembler code for function __sigsetjmp:
0X00007FFFF7A4BA80 <+0>: mov%rbx, (%rdi)//RBX into my_jum_buf.__jmp_buf[0]
0x00007ffff7a4ba83 <+3>: mov%rbp,%rax//2 lines below is the RBP register encryption, in case of Hancker, there will be reverse operation in Longjum
0x00007ffff7a4ba86 <+6>: Xor%fs:0x30,%rax//About encryption, refer to: http://hmarco.org/bugs/CVE-2013-4788.html
0x00007ffff7a4ba8f <+15>: Rol $0x11,%rax
0x00007ffff7a4ba93 <+19>: mov%rax,0x8 (%rdi)//RBP into my_jum_buf.__jmp_buf[1] because it is a 64-bit system, so it is plus 8
0x00007ffff7a4ba97 <+23>: mov%r12,0x10 (%rdi)
0x00007ffff7a4ba9b <+27>: mov%r13,0x18 (%rdi)
0x00007ffff7a4ba9f <+31>: mov%r14,0x20 (%rdi)
0x00007ffff7a4baa3 <+35>: mov%r15,0x28 (%rdi)
0x00007ffff7a4baa7 <+39>: Lea 0x8 (%RSP),%RDX//At this time, the RSP plus 8 corresponds to the RSP in main that is used for the apoptosis setjmp, because
0X00007FFFF7A4BAAC <+44>: XOR%FS:0X30,%RDX//When calling setjmp in Main, the hardware performs a stack operation on the PC, so here
0X00007FFFF7A4BAB5 <+53>: Rol $0X11,%RDX//To be reduced by 8. The RSP is then encrypted and saved to My_jum_buf.__jmp_buf[6]
0X00007FFFF7A4BAB9 <+57>: mov%rdx,0x30 (%rdi)
0X00007FFFF7A4BABD <+61>: mov (%RSP),%rax//At this point, the RSP points to the PC value that is loaded by the hardware when the SETJMP function is called
0X00007FFFF7A4BAAC <+44>: XOR%FS:0X30,%RDX//Encrypt PC and save to My_jum_buf.__jmp_buf[7]
0x00007ffff7a4bac1 <+65>: Xor%fs:0x30,%rax
0x00007ffff7a4baca <+74>: Rol $0x11,%rax
0x00007ffff7a4bace <+78>: mov%rax,0x38 (%rdi)
0x00007ffff7a4bad2 <+82>: JMPQ 0x7ffff7a4bae0 <__sigjmp_save>
End of assembler dump.
(GDB) Disassemble __sigjmp_save
Dump of assembler code for function __sigjmp_save:
0x00007ffff7df1c70 <+0>: Movl $0x0,0x40 (%rdi)
0x00007ffff7df1c77 <+7>: XOR%eax,%eax//set EAX is 0, that is, after the first call, the return value is 0
0x00007ffff7df1c79 <+9>: retq//setjmp function return, implied: mov (%ESP), $PC
End of assembler dump.
(GDB)
At this point, the values of the 8 registers have been saved to the JMPBUF.
corresponding GLIBC Source:
Sysdeps\x86_64\jmpbuf-offsets.h
#define JB_RBX 0
#define JB_RBP 1
#define JB_R12 2
#define JB_R13 3
#define JB_R14 4
#define JB_R15 5
#define JB_RSP 6
#define JB_PC 7
#define JB_SIZE (8*8)
sysdeps\x86_64\setjmp. S
ENTRY (__SIGSETJMP)/* Save registers. */Movq%RBX, (jb_rbx*8) (%rdi) #ifdef ptr_mangle movq%rbp,%rax ptr_mangle (%rax) movq%rax, (jb_rbp*8) (%rdi) #else movq%rbp, (jb_rbp*8) (%rdi) #endif movq%r12, (jb_r12*8) (%rdi) Movq%r13, (jb_r13*8) (%rdi) Movq%r14, (jb_r14*8) (%rdi) Movq%r15, (jb_r15*8) (%rdi) Leaq8(%RSP),%RDX/* Save SP as it is is after we return. */#ifdef Ptr_mangle ptr_mangle (%RDX) #endif movq%rdx, (jb_rsp*8) (%rdi) movq (%RSP),%rax/* Save PC We is returning to now. */#ifdef Ptr_mangle ptr_mangle (%rax) #endif movq%rax, (jb_pc*8) (%rdi) #if defined NOT_IN_LIBC && defined is_in_rtld/*inchld.so We never save the signal mask. */Xorl%eax,%eax retq#else/* Make a tailPagerTo __sigjmp_save;it takes the same args. */# ifdef PICjmpC_symbol_name (Bp_sym (__sigjmp_save)) @PLT # ElsejmpBp_sym (__sigjmp_save) # endif
4, SETJMP & longjmp Realization--longjmp:
Call LONGJMP's first parameter jmpbuf address, which is stored in EDI. The second parameter is stored in the ESI and this parameter will do the return value of setjmp.
Restores the corresponding registers to the data backed up in the jmpbuf. Before the setjmp is encrypted, it needs to be decrypted and then restored. For PC registers, PC loading is implemented via JMP instructions because the PC does not support assignment operations.
(GDB) Disassemble longjmp
Dump of assembler code for function __libc_siglongjmp:
.........................
0x00007ffff7a4bb54 <+36>: callq 0x7ffff7a4bb70 <__longjmp>
.........................
End of assembler dump.
(GDB) Disassemble __longjmp
Dump of assembler code for function __longjmp:
0x00007ffff7a4bb70 <+0>: mov 0x30 (%rdi),%R8//except for the value of the PC cannot be directly assigned, the other 7 registers
0x00007ffff7a4bb74 <+4>: mov 0x8 (%rdi),%R9
0x00007ffff7a4bb78 <+8>: mov 0x38 (%rdi),%rdx//pc value in RDX, followed by JMP instructions for PC Register loading
0x00007ffff7a4bb7c <+12>: Ror $0x11,%r8
0x00007ffff7a4bb80 <+16>: Xor%fs:0x30,%r8
0x00007ffff7a4bb89 <+25>: Ror $0x11,%r9
0x00007ffff7a4bb8d <+29>: Xor%fs:0x30,%r9
0x00007ffff7a4bb96 <+38>: Ror $0X11,%RDX
0X00007FFFF7A4BB9A <+42>: Xor%FS:0X30,%RDX
0x00007ffff7a4bba3 <+51>: mov (%rdi),%RBX
0x00007ffff7a4bba6 <+54>: mov 0x10 (%rdi),%r12
0x00007ffff7a4bbaa <+58>: mov 0x18 (%rdi),%r13
0x00007ffff7a4bbae <+62>: mov 0x20 (%rdi),%r14
0X00007FFFF7A4BBB2 <+66>: mov 0x28 (%rdi),%r15
0x00007ffff7a4bbb6 <+70>: mov%esi,%eax
0X00007FFFF7A4BBB8 <+72>: mov%r8,%rsp
0X00007FFFF7A4BBBB <+75>: mov%r9,%rbp
0x00007ffff7a4bbbe <+78>: JMPQ *%RDX
End of assembler dump.
(GDB)
5, why can return from the setjmp multiple times, and the return value can be set by itself
Through the above analysis, it is easy to know, because in setjmp, save the execution site, mainly is the PC and the stack pointer. When longjmp, these registers are restored, so that, once restored, it is then executed from the next instruction of the setjmp instruction.
When longjmp, the second parameter (stored in the ESI register) is assigned directly to the EAX, and the return value of the function is in eax, which is the contract for the x86 CPU.
The first instruction after setjmp returns is to determine the return value.
6, when returning from setjmp, those data is invalid, how to avoid
From the above analysis, the non-backup registers, the data is unknowable, may be contaminated.
If the data is taken out of memory, it is OK. If there is a compile-first, when read variables, not necessarily from within the access, it is possible to read from the register, at this time, need to add the keyword before the variable: volatile, guaranteed to be accessed from within each time, rather than register.
Report:
Note thatthe optimizations don ' t affect the global, static, and volatile variables; Their values after thelongjmpIs the last values of that they assumed. Thesetjmp(3) manual page on one system states this variables stored in memory would have values as of the time of thelongjmp, whereas variables in the CPU and floating-point registers is restored to their values whensetjmpwas called. This is indeed the "we see when we run the" program in Figure 7.13. Without optimization, all five variables is stored in memory (theRegisterHint is ignored forRegival). When we enable optimization, bothAutovalandRegivalGo into registers, even though the former wasn ' t declaredRegister, and thevolatileVariable stays in memory. The thing to realize and this example are so you must use thevolatileattribute if you ' re writing portable code that uses nonlocal jumps. Anything else can change from one system to the next.
SETJMP & LONGJMP Implementation Analysis