分类: 其他平台
2015-04-29 15:22:16
You can use the command x (for “examine”) to examine memory in any of several formats, independently of your program's data types.
x/nfu addr
x addr
x
n, the repeat count
The repeat count is a decimal integer; the default is 1. It specifies how much memory (counting by units u) to display.
f, the display format
The display format is one of the formats used by print (`x', `d', `u', `o', `t', `a', `c', `f', `s'), and in addition `i' (for machine instructions). The default is `x' (hexadecimal) initially. The default changes each time you use either x or print.
x -- Print as integer in hexadecimal.
d -- Print as integer in signed decimal.
u -- Print as integer in unsigned decimal.
o -- Print as integer in octal.
t -- Print as integer in binary. The letter `t' stands for “two”. 1
a -- Print as an address, both absolute in hexadecimal and as an offset from the nearest preceding symbol. You can use this format used to discover where (in what function) an unknown address is located:
(gdb) p/a 0x54320
$3 = 0x54320 <_initialize_vx+396>
c -- Regard as an integer and print it as a character constant.
f -- Regard as a floating point number and print using typical floating point syntax.
s --Regard as a string
u, the unit size -- The unit size is any of
b -- Bytes.
h -- Halfwords (two bytes).
w -- Words (four bytes). This is the initial default.
g -- Giant words (eight bytes).
Each time you specify a unit size with x, that size becomes the default unit the next time you use x. For the `i' format, the unit size is ignored and is normally not written. For the `s' format (string), the unit size defaults to `b', unless it is explicitly given. Use x /hs to display 16-bit char strings and x /ws to display 32-bit strings. The next use of x /s will again display 8-bit strings. Note that the results depend on the programming language of the current compilation unit. If the language is C, the `s' modifier will use the UTF-16 encoding while `w' will use UTF-32. The encoding is set by the programming language and cannot be altered.
Example:
x/4xw $sp -- print the four words (`w') of memory above the stack pointer in hexadecimal (`x').
x/3uh 0x54320 -- display three halfwords (h) of memory, formatted as unsigned decimal integers (`u'), starting at address 0x54320.
x/5i $pc-6 -- examines machine instructions.
The names of registers are different for each machine; use info registers to see the names used on your machine.
info registers– check register names
gdb four “standard” register:
$pc -- the program counter register, $RIP
$sp -- the stack pointer register, $RSP
$fp -- a pointer to the current stack frame, “base pointer” register, $RBP
$ps -- a register that contains the processor status
Example:
p/x $pc
x/i $pc
If displaying variable, which is a address, gdb will try to display its content(value) together. Format:
Address: content(value)
Example:
(gdb) x/x $pc -- $pc is a address
0x40071e <_Z3addii+4>: 0x10ec8348
(gdb) x/i $pc
0x40071e <_Z3addii+4>: sub $0x10,%rsp
(gdb) x/xg $pc
0x40071e <_Z3addii+4>: 0x89fc7d8910ec8348
(gdb) p $pc – print value of $pc only
$9 = (void (*)()) 0x40071e
(gdb) p/x $pc
$10 = 0x40071e
Any program only includes data and machine instructions.
Machine instructions: user code(Text), static lib code and shared lib code. We can disassemble program at any program memory.
Data: initialized data, uninitialized data (bss), Heap(malloc arena), stack and registers.
Commonly, the code(machine instruction) won’t change when running a program. It means if we track data, we can know the all program status according to machine instruction. This means gdb x command can be a general tool for program debug.
Modern operating systems and architectures provide virtual memory. Through hardware support and additional code in the operating system, virtual memory allows each user process to act as though it is the only thing running on the computer. It gives each process a completely separate address space.
Through pmap, user can check process' memory map, such as:
$ pmap 23814 # If you don't have pmap installed, use 'cat /proc/23814/maps'
It is for 64-bit systems. On 32-bit systems the shared libraries are usually found at the lowest address, followed by the text segment, then everything else.
The text, or code, segment contains the actual program and any statically linked libraries. On 64-bit systems it generally starts at 0x400000 (32-bit systems like to place it at 0x8047000).
The data and BSS segments come next. The data segment contains all initialized global variables as well as static strings (eg, those used in printf). The BSS, or "block started by segment" region holds all uninitialized global variables (those which by C convention are initialized automatically to 0).
After that comes the heap, where all memory obtained via malloc() is located. The heap grows upwards as more memory is requested.
Then we have any shared libraries such as the loader, libc, malloc, etc.
Finally we have the stack, which it should be noted grows downward as it expands.
Programs use the stack to store temporary, or automatic variables, arguments passed during a function call, and information needed to return to a previous point in the program when a function call is made.
There are also three registers that are important at this point - RBP, RSP, and RIP. RSP is the stack pointer. The stack works just like a LIFO queue with push() and pop() operations. RSP tracks the next available position in this queue.
The stack frame is essentially the region of the stack that is presently active, or that corresponds to the presently executing function. It is pointed to by RBP, the "base pointer," and is used in combination with an offset to reference all local variables. Every time a function is called, RBP is updated to point to its stack frame.
RIP is the instruction pointer. It holds the address of the instruction that the CPU just loaded and is presently executing.
The diagram above shows a snapshot of the stack for a program that is presently in func1(), which was called from main(). In order for the stack to look this way, some things must have happened when func1() was called. These steps are defined by the C calling convention.
1. The arguments to func1() were pushed onto the stack.
2. func1() was called.
3. RBP was pushed onto the stack.
4. RSP was moved to RBP.
5. Space for the local variables was allocated on the stack.
6. Local variables were set to initial values (if provided).
Steps 3 through 6 are called the function prelude.
图3 带有基指针的栈帧
The only way to enter supervisor mode is to go through predefined entry points in the kernel. One of these points is called a system call.
How system calls work on x86_64 Linux by taking a look at the kernel source, specifically arch/x86_64/kernel/entry.S where we see the following comment...
/*
* System call entry. Upto 6 arguments in registers are supported.
*
* SYSCALL does not save anything on the stack and does not change the
* stack pointer.
*/
/*
* Register setup:
* rax system call number
* rdi arg0
* rcx return address for syscall/sysret, C arg3
* rsi arg1
* rdx arg2
* r10 arg3 (--> moved to rcx for C)
* r8 arg4
* r9 arg5
* r11 eflags for syscall/sysret, temporary for C
* r12-r15,rbp,rbx saved by C code, not touched.
*
* Interrupts are off on entry.
* Only called from user space.
*
* XXX if we had a free scratch register we could save the RSP into the stack frame
* and report it properly in ps. Unfortunately we haven't.
*/
So to make a system call, you first store the syscall number in RAX, any parameters in RDI, RSI, RDX, etc, and then execute the "syscall" instruction.
图4 64位模式下通用寄存器的大小和名称
图5 在传统和兼容模式下的通用寄存器大小和名称
通用寄存器在编程时通常用于不同的用途,说明如表1所示。
表1 通用寄存器的用途
寄存器名 |
用途 |
EAX |
累加器 |
EBX |
基址寄存器 |
ECX |
计数器 |
EDX |
数据寄存器 |
ESI |
源地址指针寄存器 |
EDI |
目的地址指针寄存器 |
EBP |
基址指针寄存器 |
ESP |
堆栈指针寄存器 |
Example
Test ENV
nochen@bclnx64 ~/test$ uname -a
Linux bclnx64 2.6.9-34.ELsmp #1 SMP Thu Mar 9 06:23:23 GMT 2006 x86_64 x86_64 x86_64 GNU/Linux
nochen@bclnx64 ~/test$ g++ -v
Reading specs from /usr/lib/gcc/x86_64-redhat-linux/3.4.5/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-java-awt=gtk --host=x86_64-redhat-linux
Thread model: posix
gcc version 3.4.5 20051201 (Red Hat 3.4.5-2)
nochen@bclnx64 ~/test$ gdb -v
GNU gdb Red Hat Linux (6.3.0.0-1.96rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Example 1
Aim: basic stack usage, basic assemble code
#include
#include
#include
#include
int Add(int a, int b, int c){
int arr[4] = {0};
arr[0] = a;
arr[1] = b;
arr[2] = c;
arr[3] = a+b+c;
return arr[3];
}
int main() {
int a = 0x100;
int b = 0x200;
int c = 0x345;
Add(a,b,c);
return 1;
}
gdb ./test
(gdb) x/40i Add-2
0x400506
: nop 0x400507
: nop 0x400508 <_Z3Addiii>: push %rbp
0x400509 <_Z3Addiii+1>: mov %rsp,%rbp
0x40050c <_Z3Addiii+4>: mov %edi,0xfffffffffffffffc(%rbp)
0x40050f <_Z3Addiii+7>: mov %esi,0xfffffffffffffff8(%rbp)
0x400512 <_Z3Addiii+10>: mov %edx,0xfffffffffffffff4(%rbp)
0x400515 <_Z3Addiii+13>: movq $0x0,0xffffffffffffffe0(%rbp)
0x40051d <_Z3Addiii+21>: movq $0x0,0xffffffffffffffe8(%rbp)
0x400525 <_Z3Addiii+29>: mov 0xfffffffffffffffc(%rbp),%eax
0x400528 <_Z3Addiii+32>: mov %eax,0xffffffffffffffe0(%rbp)
0x40052b <_Z3Addiii+35>: mov 0xfffffffffffffff8(%rbp),%eax
0x40052e <_Z3Addiii+38>: mov %eax,0xffffffffffffffe4(%rbp)
0x400531 <_Z3Addiii+41>: mov 0xfffffffffffffff4(%rbp),%eax
0x400534 <_Z3Addiii+44>: mov %eax,0xffffffffffffffe8(%rbp)
0x400537 <_Z3Addiii+47>: mov 0xfffffffffffffff8(%rbp),%eax
0x40053a <_Z3Addiii+50>: add 0xfffffffffffffffc(%rbp),%eax
0x40053d <_Z3Addiii+53>: add 0xfffffffffffffff4(%rbp),%eax
0x400540 <_Z3Addiii+56>: mov %eax,0xffffffffffffffec(%rbp)
0x400543 <_Z3Addiii+59>: mov 0xffffffffffffffec(%rbp),%eax
0x400546 <_Z3Addiii+62>: leaveq
0x400547 <_Z3Addiii+63>: retq
0x400548
: push %rbp 0x400549
: mov %rsp,%rbp 0x40054c
: sub $0x10,%rsp 0x400550
: movl $0x100,0xfffffffffffffffc(%rbp) 0x400557
: movl $0x200,0xfffffffffffffff8(%rbp) 0x40055e
: movl $0x345,0xfffffffffffffff4(%rbp) 0x400565
: mov 0xfffffffffffffff4(%rbp),%edx 0x400568
: mov 0xfffffffffffffff8(%rbp),%esi 0x40056b
: mov 0xfffffffffffffffc(%rbp),%edi 0x40056e
: callq 0x400508 <_Z3Addiii> 0x400573
: mov $0x1,%eax 0x400578
: leaveq 0x400579
: retq 0x40057a
: nop (gdb) b Add
Breakpoint 1 at 0x400515: file stack2.cc, line 20.
(gdb) r
Starting program: /home/nochen/test/test
Breakpoint 1, Add (a=256, b=512, c=837) at stack2.cc:20
20 int arr[4] = {0};
(gdb) info register
rax 0x645 1605
rbx 0x0 0
rcx 0x20 32
rdx 0x345 837
rsi 0x200 512
rdi 0x100 256
rbp 0x7fbffff480 0x7fbffff480
rsp 0x7fbffff480 0x7fbffff480
r8 0x7fbffff4d0 548682069200
rip 0x400543 0x400543
eflags 0x302 770
(gdb) x/60gx $sp-32
0x7fbffff460: 0x0000020000000100 0x0000064500000345
0x7fbffff470: 0x00000345f5614c40 0x0000010000000200 // Add()ocal variables
0x7fbffff480: 0x0000007fbffff4a0 0x0000000000400573 //current rbp - last rbp - ret add
0x7fbffff490: 0x0000034500000000 0x0000010000000200 //main()ocal variables
0x7fbffff4a0: 0x0000003cf5b31738 0x0000003cf591c4bb //main rbp
0x7fbffff4b0: 0x0000000000400450 0x0000007fbffff578
0x7fbffff4c0: 0x0000000100000000 0x0000000000400548
Example 2
Aim: basic stack usage, basic assemble code
#include
#include
#include
#include
int Add(int a, int b, int c){
return a+b+c;
}
int main() {
int a = 0x100;
int b = 0x200;
int c = 0x345;
Add(a,b,c);
return 1;
}
gdb ./test
(gdb) x/40i Add
0x400508 <_Z3Addiii>: push %rbp
0x400509 <_Z3Addiii+1>: mov %rsp,%rbp
0x40050c <_Z3Addiii+4>: mov %edi,0xfffffffffffffffc(%rbp)
0x40050f <_Z3Addiii+7>: mov %esi,0xfffffffffffffff8(%rbp)
0x400512 <_Z3Addiii+10>: mov %edx,0xfffffffffffffff4(%rbp)
0x400515 <_Z3Addiii+13>: mov 0xfffffffffffffff8(%rbp),%eax
0x400518 <_Z3Addiii+16>: add 0xfffffffffffffffc(%rbp),%eax
0x40051b <_Z3Addiii+19>: add 0xfffffffffffffff4(%rbp),%eax
0x40051e <_Z3Addiii+22>: leaveq
0x40051f <_Z3Addiii+23>: retq
0x400520
: push %rbp 0x400521
: mov %rsp,%rbp 0x400524
: sub $0x10,%rsp 0x400528
: movl $0x100,0xfffffffffffffffc(%rbp) 0x40052f
: movl $0x200,0xfffffffffffffff8(%rbp) 0x400536
: movl $0x345,0xfffffffffffffff4(%rbp) 0x40053d
: mov 0xfffffffffffffff4(%rbp),%edx 0x400540
: mov 0xfffffffffffffff8(%rbp),%esi 0x400543
: mov 0xfffffffffffffffc(%rbp),%edi 0x400546
: callq 0x400508 <_Z3Addiii> 0x40054b
: mov $0x1,%eax 0x400550
: leaveq 0x400551
: retq 0x400552
: nop (gdb) b Add
Breakpoint 1 at 0x400515: file stack2.cc, line 29.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/nochen/test/test
Breakpoint 1, Add (a=256, b=512, c=837) at stack2.cc:29
29 return a+b+c;
(gdb) info register
rax 0x0 0
rbx 0x0 0
rcx 0x20 32
rdx 0x345 837
rsi 0x200 512
rdi 0x100 256
rbp 0x7fbffff480 0x7fbffff480
rsp 0x7fbffff480 0x7fbffff480
r8 0x7fbffff4d0 548682069200
rip 0x400515 0x400515
eflags 0x206 518
(gdb) s
30 }
(gdb) x/16gx 0x7fbffff480-32
0x7fbffff460: 0x0000003cf87dfb00 0x0000000000000000
0x7fbffff470: 0x00000345f5614c40 0x0000010000000200
0x7fbffff480: 0x0000007fbffff4a0 0x000000000040054b
0x7fbffff490: 0x0000034500000000 0x0000010000000200
0x7fbffff4a0: 0x0000003cf5b31738 0x0000003cf591c4bb
0x7fbffff4b0: 0x0000000000400450 0x0000007fbffff578
0x7fbffff4c0: 0x0000000100000000 0x0000000000400520
0x7fbffff4d0: 0x0000000000000000 0x0000003cf5614c40
Example 3
Aim: a case for crashing stack
#include
int main (int argc, char** argv ) {
execve("/bin/sh", NULL, NULL); // correct: execve("/bin/sh", argv, NULL);
}
gdb ./test
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400570
: push %rbp 0x0000000000400571
: mov %rsp,%rbp 0x0000000000400574
: sub $0x10,%rsp 0x0000000000400578
: mov %edi,0xfffffffffffffffc(%rbp) 0x000000000040057b
: mov %rsi,0xfffffffffffffff0(%rbp) 0x000000000040057f
: mov $0x0,%edx 0x0000000000400584
: mov $0x0,%esi 0x0000000000400589
: mov $0x40068c,%edi 0x000000000040058e
: callq 0x400480 // Use ‘disassemble execve’to get execve code // a little different with: 0x400480 0x0000000000400593
: mov $0x1,%eax 0x0000000000400598
: leaveq 0x0000000000400599
: retq End of assembler dump.
(gdb) r
Starting program: /home/nochen/test/test
Program received signal SIGSEGV, Segmentation fault.
0x0000000000418969 in ?? ()
(gdb) bt
#0 0x0000000000418969 in ?? ()
#1 0x0000000000000000 in ?? ()
(gdb) info register
rax 0x0 0
rbx 0x0 0
rcx 0x3cf592e813 261818083347
rdx 0x0 0
rsi 0x0 0
rdi 0x0 0
rbp 0x5b4870 0x5b4870 // rbp is wrong. I think Stack crash
rsp 0x7fbffffbb0 0x7fbffffbb0
rip 0x418969 0x418969
eflags 0x10246 66118
(gdb) x/16gx 0x5b4870-16
0x5b4860: 0x0000003cf5b2e680 0x0000000000000000
0x5b4870: 0x0000007fbffffee0 0x0000003cf5b2eb00
0x5b4880: 0x0000000000000000 0x0000000000000000
0x5b4890: 0x0000003cf5b2e8c0 0x0000000000000000
0x5b48a0: 0x0000000000000000 0x0000000000000000
0x5b48b0: 0x0000000000000000 0x0000000000000000
0x5b48c0: 0x0000000000000000 0x0000000000000000
0x5b48d0: 0x0000000000000000 0x0000000000000000
(gdb) x/16gx 0x0000007fbffffee0-16
0x7fbffffed0: 0x0000000000000000 0x0000000000000000
0x7fbffffee0: 0x0000000000000000 0x0000000000000010 // wrong last rbp addr
0x7fbffffef0: 0x00000000bfebfbff 0x0000000000000006
0x7fbfffff00: 0x0000000000001000 0x0000000000000011
0x7fbfffff10: 0x0000000000000064 0x0000000000000003
0x7fbfffff20: 0x0000000000400040 0x0000000000000004
0x7fbfffff30: 0x0000000000000038 0x0000000000000005
0x7fbfffff40: 0x0000000000000009 0x0000000000000007
(gdb) x/16i 0x0000000000418969-8
0x418961: add %cl,0xffffffffffffff89(%rax)
0x418964: add $0x19bf4f,%eax
0x418969: cmpb $0x2d,(%rax)
0x41896c: je 0x418c46
0x418972: mov 1687359(%rip),%rdx # 0x5b48b8
0x418979: cmpb $0x73,(%rdx)
0x41897c: je 0x418c21
0x418982: mov 1687343(%rip),%rdx # 0x5b48b8
0x418989: cmpb $0x73,(%rdx)
0x41898c: je 0x418bfa
0x418992: mov 1705607(%rip),%rdi # 0x5b9020
0x418999: mov %rbx,1687320(%rip) # 0x5b48b8
0x4189a0: test %rdi,%rdi
0x4189a3: jne 0x418bee
0x4189a9: mov 1687304(%rip),%rdi # 0x5b48b8
0x4189b0: callq 0x417380
(gdb)
For this case, I don't know how to debug it accord to these information!
Example 4
Aim: for dead-lock case.
#include
#include
#include
#include
#include
pthread_t ntid;
class Mutex
{
public:
Mutex(bool recursive = false) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,
recursive ? PTHREAD_MUTEX_RECURSIVE : PTHREAD_MUTEX_ERRORCHECK );
pthread_mutex_init(&d_mutex, &attr);
pthread_mutexattr_destroy(&attr);
}
~Mutex() { pthread_mutex_destroy(&d_mutex); }
void Lock() { pthread_mutex_lock(&d_mutex); }
void Lock_Cancel() { pthread_testcancel(); pthread_mutex_lock(&d_mutex); }
void Unlock() { pthread_mutex_unlock(&d_mutex); }
bool TryLock() { return pthread_mutex_trylock(&d_mutex) == 0; }
private:
pthread_mutex_t d_mutex;
};
void printids(const char *s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid,
(unsigned int)tid, (unsigned int)tid);
}
Mutex locker, locker2;
void * thr_fn(void *arg)
{
int count = 1000;
if(arg != NULL)
count = *((int*)arg);
locker.Lock();
int i=0;
for(i=0; i
{
printf("Func 1 Time %d : ", i);
locker2.Lock();
printids("new thread: ");
sleep(1);
locker2.Unlock();
}
locker.Unlock();
return((void *)0);
}
int main(void)
{
int err;
pthread_t tid1, tid2;
void* tret;
int count = 10;
printf("PID=%d\n", getpid());
// thread 1
err = pthread_create(&tid1, NULL, thr_fn, NULL);
if (err != 0)
{
printf("can't create thread : %s\n", strerror(err));
}
printf("Create thread 1 : %u \n", (unsigned int)tid1);
// thread 2
err = pthread_create(&tid2, NULL, thr_fn, &count);
if (err != 0)
{
printf("can't create thread : %s\n", strerror(err));
}
printf("Create thread 2 : %u \n", (unsigned int)tid2);
printids("main thread:");
sleep(1);
pthread_cancel(tid1);
printf("Cancel thread 1\n");
err = pthread_join(tid1, &tret);
if (err != 0)
printf("can't join with thread 1: %s\n", strerror(err));
printf("thread 1 exit code %d\n", (int)tret);
err = pthread_join(tid2, &tret);
if (err != 0)
printf("can't join with thread 2: %s\n", strerror(err));
printf("thread 2 exit code %d\n", (int)tret);
return 0;
}
g++ -o test deadlock.cc –lpthread
(shell 1)nochen@bclnx64 ~/test$ ./test
PID=4249
Create thread 1 : 1084229984
Create thread 2 : 1094719840
main thread: pid 4249 tid 2505602208 (0x955874a0)
Func 1 Time 0 : new thread: pid 4249 tid 1084229984 (0x40a00960)
Cancel thread 1
thread 1 exit code -1
(shell 2)gdb ./test
(gdb) attach 4249
(gdb) info thread
2 Thread 1094719840 (LWP 4251) 0x0000003cf620adfb in __lll_mutex_lock_wait () from /lib64/tls/libpthread.so.0
1 Thread 182894228640 (LWP 4249) 0x0000003cf6206ffb in pthread_join () from /lib64/tls/libpthread.so.0
(gdb) bt
#0 0x0000003cf6206ffb in pthread_join () from /lib64/tls/libpthread.so.0
#1 0x0000000000400d8e in main ()
(gdb) thread 2
[Switching to thread 2 (Thread 1094719840 (LWP 4251))]#0 0x0000003cf620adfb in __lll_mutex_lock_wait ()
from /lib64/tls/libpthread.so.0
(gdb) bt
#0 0x0000003cf620adfb in __lll_mutex_lock_wait () from /lib64/tls/libpthread.so.0
#1 0x00000000414011e0 in ?? ()
#2 0x00000000414019f0 in ?? ()
#3 0x0000003cf6207bd4 in pthread_mutex_lock () from /lib64/tls/libpthread.so.0
#4 0x0000000000000000 in ?? ()
(gdb) info register
rax 0xfffffffffffffffc -4
rbx 0x0 0
rcx 0xffffffffffffffff -1
rdx 0x2 2
rsi 0x0 0
rdi 0x501740 5248832
rbp 0x414011b0 0x414011b0
rsp 0x41401100 0x41401100
rip 0x3cf620adfb 0x3cf620adfb <__lll_mutex_lock_wait+27>
eflags 0x202 514
(gdb) x/16gx 0x414011b0 - 16
0x414011a0: 0x0000000000000000 0x0000000000501740
0x414011b0: 0x00000000414011d0 0x0000000000400b4d
0x414011c0: 0x0000000a00000000 0x0000007fbffff3cc
0x414011d0: 0x0000000000000000 0x0000003cf620610a (not useful information)
0x414011e0: 0x0000000000000000 0x0000000041401960
0x414011f0: 0x0000000041401960 0x0000000000000000
0x41401200: 0x0000003cf6206080 0x0000003cf620d4e0
0x41401210: 0x0000000000000000 0x0000003cf620d4e0
(gdb) x/32i 0x0000000000400b4d - 4
0x400b49 <_Z6thr_fnPv+41>: push %rbx
0x400b4a <_Z6thr_fnPv+42>: add (%rax),%eax
0x400b4c <_Z6thr_fnPv+44>: add %al,%bh
0x400b4e <_Z6thr_fnPv+46>: rexXZ lock add %al,(%rax)
0x400b52 <_Z6thr_fnPv+50>: add %al,(%rax)
0x400b54 <_Z6thr_fnPv+52>: movl $0x0,0xfffffffffffffff0(%rbp)
0x400b5b <_Z6thr_fnPv+59>: mov 0xfffffffffffffff0(%rbp),%eax
0x400b5e <_Z6thr_fnPv+62>: cmp 0xfffffffffffffff4(%rbp),%eax
0x400b61 <_Z6thr_fnPv+65>: jge 0x400ba5 <_Z6thr_fnPv+133>
0x400b63 <_Z6thr_fnPv+67>: mov 0xfffffffffffffff0(%rbp),%esi
0x400b66 <_Z6thr_fnPv+70>: mov $0x401035,%edi
0x400b6b <_Z6thr_fnPv+75>: mov $0x0,%eax
From these information, we can know that the dead-lock is in function thr_fnPv+44 and thr_fnPv+46。 Thus we know it is because thread 2(1094719840) can’t get lock!
Example 5
Aim: for dead-lock case.
#include
#include
#include
#include
#include
pthread_t ntid;
class Mutex
{
public:
Mutex(bool recursive = false) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,
recursive ? PTHREAD_MUTEX_RECURSIVE : PTHREAD_MUTEX_ERRORCHECK );
pthread_mutex_init(&d_mutex, &attr);
pthread_mutexattr_destroy(&attr);
}
~Mutex() { pthread_mutex_destroy(&d_mutex); }
void Lock() { pthread_mutex_lock(&d_mutex); }
void Lock_Cancel() { pthread_testcancel(); pthread_mutex_lock(&d_mutex); }
void Unlock() { pthread_mutex_unlock(&d_mutex); }
bool TryLock() { return pthread_mutex_trylock(&d_mutex) == 0; }
private:
pthread_mutex_t d_mutex;
};
void printids(const char *s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid,
(unsigned int)tid, (unsigned int)tid);
}
Mutex locker, locker2;
void * thr_fn(void *arg)
{
int count = 1000;
if(arg != NULL)
count = *((int*)arg);
locker.Lock();
int i=0;
for(i=0; i
{
printf("Func 1 Time %d : ", i);
locker2.Lock();
printids("new thread: ");
sleep(1);
locker2.Unlock();
}
locker.Unlock();
return((void *)0);
}
void * thr_fn2(void *arg)
{
int count = 1000;
if(arg != NULL)
count = *((int*)arg);
locker2.Lock();
int i=0;
for(i=0; i
{
printf("Func2 Time %d : ", i);
locker.Lock();
printids("new thread: ");
sleep(1);
locker.Unlock();
}
locker2.Unlock();
return((void *)0);
}
int main(void)
{
int err;
pthread_t tid1, tid2;
void* tret;
int count = 10;
printf("PID=%d\n", getpid());
// thread 1
err = pthread_create(&tid1, NULL, thr_fn, NULL);
if (err != 0)
{
printf("can't create thread : %s\n", strerror(err));
}
printf("Create thread 1 : %u \n", (unsigned int)tid1);
// thread 2
err = pthread_create(&tid2, NULL, thr_fn2, &count);
if (err != 0)
{
printf("can't create thread : %s\n", strerror(err));
}
printf("Create thread 2 : %u \n", (unsigned int)tid2);
printids("main thread:");
sleep(1);
// pthread_cancel(tid1);
// printf("Cancel thread 1\n");
err = pthread_join(tid1, &tret);
if (err != 0)
printf("can't join with thread 1: %s\n", strerror(err));
printf("thread 1 exit code %d\n", (int)tret);
// thread 2
/* err = pthread_create(&tid2, NULL, thr_fn, &count);
if (err != 0)
{
printf("can't create thread : %s\n", strerror(err));
}
printf("Create thread 2 : %u \n", (unsigned int)tid2); */
////////////////////////////////
err = pthread_join(tid2, &tret);
if (err != 0)
printf("can't join with thread 2: %s\n", strerror(err));
printf("thread 2 exit code %d\n", (int)tret);
return 0;
}
Shell 1:
g++ -g -o test deadlock.cc –lpthread
nochen@bclnx64 ~/test$ ./test
PID=6283
Create thread 1 : 1084229984
Create thread 2 : 1094719840
main thread: pid 6283 tid 2505602208 (0x955874a0)
Func 1 Time 0 : new thread: pid 6283 tid 1084229984 (0x40a00960)
Shell 2:
gdb ./test
(gdb) attach 6283
(gdb) info thread
3 Thread 1084229984 (LWP 6284) 0x0000003cf620adfb in __lll_mutex_lock_wait () from /lib64/tls/libpthread.so.0
2 Thread 1094719840 (LWP 6285) 0x0000003cf620adfb in __lll_mutex_lock_wait () from /lib64/tls/libpthread.so.0
* 1 Thread 182894228640 (LWP 6283) 0x0000003cf6206ffb in pthread_join () from /lib64/tls/libpthread.so.0
(gdb) thread 2
[Switching to thread 2 (Thread 1094719840 (LWP 6285))]#0 0x0000003cf620adfb in __lll_mutex_lock_wait ()
from /lib64/tls/libpthread.so.0
(gdb) info register
rax 0xfffffffffffffffc -4
rbx 0x0 0
rcx 0xffffffffffffffff -1
rdx 0x2 2
rsi 0x0 0
rdi 0x5016a0 5248672
rbp 0x414011b0 0x414011b0
rsp 0x41401100 0x41401100
rip 0x3cf620adfb 0x3cf620adfb <__lll_mutex_lock_wait+27>
eflags 0x202 514
(gdb) x/32gx 0x414011b0 - 16
0x414011a0: 0x0000000000000000 0x00000000005016a0
0x414011b0: 0x00000000414011d0 0x0000000000400bc5
0x414011c0: 0x0000000a00000000 0x0000007fbffff3cc
0x414011d0: 0x0000000000000000 0x0000003cf620610a
0x414011e0: 0x0000000000000000 0x0000000041401960
0x414011f0: 0x0000000041401960 0x0000000000000000
(gdb) x/32i 0x0000000000400bc5 - 2
0x400bc3 <_Z7thr_fn2Pv+93>: add %al,(%rax)
0x400bc5 <_Z7thr_fn2Pv+95>: mov $0x400fd7,%edi
0x400bca <_Z7thr_fn2Pv+100>: callq 0x400a88 <_Z8printidsPKc>
0x400bcf <_Z7thr_fn2Pv+105>: mov $0x1,%edi
0x400bd4 <_Z7thr_fn2Pv+110>: callq 0x400938
0x400bd9 <_Z7thr_fn2Pv+115>: mov $0x5016a0,%edi
0x400bde <_Z7thr_fn2Pv+120>: callq 0x400e20 <_ZN5Mutex6UnlockEv>
0x400be3 <_Z7thr_fn2Pv+125>: lea 0xfffffffffffffff0(%rbp),%rax
0x400be7 <_Z7thr_fn2Pv+129>: incl (%rax)
0x400be9 <_Z7thr_fn2Pv+131>: jmp 0x400ba1 <_Z7thr_fn2Pv+59>
0x400beb <_Z7thr_fn2Pv+133>: mov $0x5016e0,%edi
0x400bf0 <_Z7thr_fn2Pv+138>: callq 0x400e20 <_ZN5Mutex6UnlockEv>
0x400bf5 <_Z7thr_fn2Pv+143>: mov $0x0,%eax
0x400bfa <_Z7thr_fn2Pv+148>: leaveq
0x400bfb <_Z7thr_fn2Pv+149>: retq
(gdb) thread 3
[Switching to thread 3 (Thread 1084229984 (LWP 6284))]#0 0x0000003cf620adfb in __lll_mutex_lock_wait ()
from /lib64/tls/libpthread.so.0
(gdb) info register
rax 0xfffffffffffffffc -4
rbx 0x0 0
rcx 0xffffffffffffffff -1
rdx 0x2 2
rsi 0x0 0
rdi 0x5016e0 5248736
rbp 0x40a001b0 0x40a001b0
rsp 0x40a00100 0x40a00100
rip 0x3cf620adfb 0x3cf620adfb <__lll_mutex_lock_wait+27>
eflags 0x202 514
(gdb) x/16gx 0x40a001b0 - 16
0x40a001a0: 0x0000000040a001d0 0x00000000005016e0
0x40a001b0: 0x0000000040a001d0 0x0000000000400b2f
0x40a001c0: 0x000003e800000001 0x0000000000000000
0x40a001d0: 0x0000000000000000 0x0000003cf620610a
0x40a001e0: 0x0000000000000000 0x0000000040a00960
0x40a001f0: 0x0000000040a00960 0x0000000000000000
0x40a00200: 0x0000003cf6206080 0x0000003cf620d4e0
0x40a00210: 0x0000000000000000 0x0000003cf620d4e0
(gdb) x/32i 0x0000000000400b2f-2
0x400b2d <_Z6thr_fnPv+93>: add %al,(%rax)
0x400b2f <_Z6thr_fnPv+95>: mov $0x400fd7,%edi
0x400b34 <_Z6thr_fnPv+100>: callq 0x400a88 <_Z8printidsPKc>
0x400b39 <_Z6thr_fnPv+105>: mov $0x1,%edi
0x400b3e <_Z6thr_fnPv+110>: callq 0x400938
0x400b43 <_Z6thr_fnPv+115>: mov $0x5016e0,%edi
0x400b48 <_Z6thr_fnPv+120>: callq 0x400e20 <_ZN5Mutex6UnlockEv>
0x400b4d <_Z6thr_fnPv+125>: lea 0xfffffffffffffff0(%rbp),%rax
0x400b51 <_Z6thr_fnPv+129>: incl (%rax)
0x400b53 <_Z6thr_fnPv+131>: jmp 0x400b0b <_Z6thr_fnPv+59>
0x400b55 <_Z6thr_fnPv+133>: mov $0x5016a0,%edi
0x400b5a <_Z6thr_fnPv+138>: callq 0x400e20 <_ZN5Mutex6UnlockEv>
0x400b5f <_Z6thr_fnPv+143>: mov $0x0,%eax
0x400b64 <_Z6thr_fnPv+148>: leaveq
0x400b65 <_Z6thr_fnPv+149>: retq
0x400b66 <_Z7thr_fn2Pv>: push %rbp
0x400b67 <_Z7thr_fn2Pv+1>: mov %rsp,%rbp
0x400b6a <_Z7thr_fn2Pv+4>: sub $0x10,%rsp
0x400b6e <_Z7thr_fn2Pv+8>: mov %rdi,0xfffffffffffffff8(%rbp)
0x400b72 <_Z7thr_fn2Pv+12>: movl $0x3e8,0xfffffffffffffff4(%rbp)
0x400b79 <_Z7thr_fn2Pv+19>: cmpq $0x0,0xfffffffffffffff8(%rbp)
0x400b7e <_Z7thr_fn2Pv+24>: je 0x400b89 <_Z7thr_fn2Pv+35>
0x400b80 <_Z7thr_fn2Pv+26>: mov 0xfffffffffffffff8(%rbp),%rax
0x400b84 <_Z7thr_fn2Pv+30>: mov (%rax),%eax
0x400b86 <_Z7thr_fn2Pv+32>: mov %eax,0xfffffffffffffff4(%rbp)
0x400b89 <_Z7thr_fn2Pv+35>: mov $0x5016e0,%edi
0x400b8e <_Z7thr_fn2Pv+40>: callq 0x400e38 <_ZN5Mutex4LockEv>
0x400b93 <_Z7thr_fn2Pv+45>: movl $0x0,0xfffffffffffffff0(%rbp)
0x400b9a <_Z7thr_fn2Pv+52>: movl $0x0,0xfffffffffffffff0(%rbp)
0x400ba1 <_Z7thr_fn2Pv+59>: mov 0xfffffffffffffff0(%rbp),%eax
0x400ba4 <_Z7thr_fn2Pv+62>: cmp 0xfffffffffffffff4(%rbp),%eax
0x400ba7 <_Z7thr_fn2Pv+65>: jge 0x400beb <_Z7thr_fn2Pv+133>
(gdb) Quit
Thus we know function thr_fn and thr_fn2 is dead-lock!