首页 > 其他分享 >gdb

gdb

时间:2024-10-06 20:43:53浏览次数:1  
标签:调用 函数 查看 gdb 线程 断点

gdb控制程序行为

  • 环境变量

    (gdb) set environment VAR=value

多线程

相关命令

  • 查看线程列表:在 gdb 中可以使用以下命令查看当前所有线程的列表:

    (gdb) info threads
    

    这将列出所有线程及其状态,包括它们的 LWP ID 和线程标识符。

  • 切换到特定线程:如果你想切换调试上下文到某个特定的线程,可以使用:

    (gdb) thread <thread_number>
    

    其中 <thread_number>gdb 为线程分配的编号(可以从 info threads 命令中获取)。

  • 查看所有线程的调用栈(批量查看每个线程的栈帧):

    (gdb) thread apply all bt
    

    这会列出所有线程的调用栈信息,方便你快速发现哪个线程挂掉或崩溃。

使用 pstack 工具查看线程栈
如果你只想快速查看某个进程的所有线程的栈,而不需要全面调试,可以使用 pstack 工具(需要安装)。它会显示所有线程的调用栈(包括 LWP ID)。

通过 pstop 查看线程状态

gdb 之外,你也可以通过系统命令查看线程的状态:

  • 使用 ps 查看线程状态

    bash

    Copy

    ps -eLf | grep <PID>
    

    该命令会列出指定进程的所有线程,并显示线程的状态、PID、LWP 等信息。你可以通过 S 列查看线程的状态:

    • R:正在运行。
    • S:休眠中。
    • D:不可中断的睡眠(通常是 I/O 操作)。
    • Z:僵尸线程。
  • 使用 top 查看线程状态

    启动 top 命令并按 H 键,可以查看某个进程的所有线程及其状态。

    bash

    Copy

    top -H -p <PID>
    

    这将显示该进程的所有线程及其 CPU 使用率、状态等信息。

总结

  • 使用 gdb 中的 info threads 命令可以查看所有线程的状态。
  • 使用 thread <id> 切换到特定线程,使用 backtrace 查看其调用栈。
  • 使用 thread apply all bt 可以一次性查看所有线程的调用栈,快速定位问题线程。
  • 通过调用栈可以识别线程是否因段错误、死锁、或其他系统调用而挂起。
  • 使用系统工具如 pstop 也可以查看线程的状态,辅助判断哪个线程可能挂掉了。

信号

SIGABRT 信号通常在以下情景下触发:

  • 显式调用 abort()
  • 未通过 assert 检查
  • 内存管理错误(如非法释放内存或越界访问)。
  • 未捕获的异常(在 C++ 中,抛出异常但未捕获时会调用 std::terminate(),进而触发 SIGABRT)。
  • 手动发送 SIGABRT 信号

情境问题

如何知道一个文件被哪些进程的哪些函数读取过?

要知道一个文件被哪些进程的哪些函数读取过,这个问题涉及两部分:

  1. 哪些进程读取了该文件:这是系统级别的问题,通过跟踪文件系统调用可以获取相关信息。
  2. 哪些函数读取了该文件:这是进程内部的问题,需要了解进程内的调用栈。

要实现这两部分的监控,通常需要结合系统工具和调试工具来完成。以下是几种常见的方式:

1. 使用 strace 跟踪文件访问系统调用

strace 是 Linux 系统中的一个工具,可以跟踪进程的系统调用,包括 open(), read(), write() 等文件操作的系统调用。通过 strace,你可以知道某个文件被哪些进程读取过。

步骤:

  1. 跟踪所有进程对文件的访问

    你可以通过 strace 跟踪所有进程的文件系统调用,并过滤出对特定文件的访问。例如,假设你想跟踪 /path/to/file 文件的访问,可以使用以下命令:

    sudo strace -f -e trace=open,read,write -p $(pgrep -d, .) 2>&1 | grep "/path/to/file"
    

    解释:

    • -f:跟踪子进程。
    • -e trace=open,read,write:只跟踪 open(), read(), write() 系统调用。
    • -p $(pgrep -d, .):跟踪所有进程(pgrep -d, . 会输出所有进程的 PID)。
  2. 跟踪特定进程的文件访问

    如果你知道某个进程的 PID,你可以使用 strace 直接跟踪该进程:

    sudo strace -f -e trace=open,read,write -p <PID>
    

    这将显示该进程打开、读取、写入的文件。你可以结合 grep 过滤出你感兴趣的文件。

  3. 输出示例

    假设有个进程在读取 /path/to/filestrace 的输出可能如下:

    open("/path/to/file", O_RDONLY) = 3
    read(3, "file contents...", 1024) = 1024
    

    这表明进程通过文件描述符 3 打开了文件 /path/to/file 并读取了 1024 字节的内容。

总结

  • strace:可以用来跟踪进程对文件的系统调用,知道哪些进程访问了文件。
  • lsof:可以实时查看哪些进程打开了文件。
  • auditd:可以对文件访问进行审计,记录哪些进程访问了文件。
  • gdbptrace:可以用来跟踪进程内部的函数调用栈,了解哪些函数读取了文件。
  • LD_PRELOAD:可以通过插桩方式重载文件操作函数,捕捉调用栈信息。

通过结合这些工具,你可以追踪文件访问的进程和函数调用栈。

使用 lsof 查看文件被哪些进程打开

lsof 是一个用于列出打开文件的工具。你可以使用它来查看某个文件当前被哪些进程打开。

步骤:

lsof /path/to/file

这将输出所有当前打开该文件的进程信息,包括进程 ID、进程名称、打开的文件描述符等。例如:

COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF  NODE NAME
cat      1234  user    3r   REG  8,1    1234     5678 /path/to/file

这里,cat 进程(PID 1234)以只读模式(3r)打开了 /path/to/file

3. 使用 auditd 进行文件访问审计

auditd 是 Linux 的审计框架,可以对文件访问进行详细的日志记录,包括进程访问文件的情况。

步骤:

  1. 安装 auditd

    如果未安装 auditd,可以使用以下命令安装:

    sudo apt install auditd
    
  2. 添加审计规则

    使用 auditctl 添加审计规则,跟踪某个文件的读取:

    sudo auditctl -w /path/to/file -p r -k file_read
    

    解释:

    • -w /path/to/file:监控文件 /path/to/file
    • -p r:监控读取操作。
    • -k file_read:为此规则设置一个标识符 file_read
  3. 查看审计日志

    审计日志会存储在 /var/log/audit/audit.log 文件中。你可以使用 ausearch 命令查看与文件访问相关的日志:

    sudo ausearch -k file_read
    

    这将显示哪些进程读取了 /path/to/file,包括详细的进程信息。

4. 使用 gdbptrace 获取函数调用栈

straceauditd 可以告诉你哪些进程访问了某个文件,但它们无法告诉你进程内部的函数调用栈。如果你想知道进程内部的哪些函数读取了该文件,则需要使用调试器(如 gdb)或 ptrace 进行更深入的分析。

步骤:

  1. 使用 gdb 附加到进程

    假设你已经知道某个进程(PID 为 <PID>)在访问文件,你可以使用 gdb 附加到该进程并设置断点在 open(), read(), 或 fopen() 等文件操作函数上:

    gdb -p <PID>
    
  2. 设置断点

    gdb 中设置断点,例如你想捕捉 open() 系统调用:

    (gdb) break open
    

    如果使用 C 标准库的 fopen() 函数:

    (gdb) break fopen
    
  3. 继续执行并捕捉调用栈

    继续执行进程,直到遇到断点:

    (gdb) continue
    

    当进程命中断点时,你可以使用 backtrace 命令查看当前的函数调用栈:

    (gdb) backtrace
    

    这将显示当前函数及其父函数,帮助你确定哪些函数调用了 open()read()

  4. 输出示例

    假设调用栈如下:

    #0  open (filename="/path/to/file", flags=O_RDONLY) at open.c:34
    #1  0x00007ffff7abc123 in read_file() at file_reader.c:54
    #2  0x00007ffff7abc456 in main() at main.c:12
    

    这表示 main() 调用了 read_file(),而 read_file() 又调用了 open() 打开了 /path/to/file

5. 自定义动态库插桩 (LD_PRELOAD)

可以通过插桩(interpose)标准库函数 open, read, fopen 等,来记录下文件访问的详细信息。你可以编写一个自定义的动态库,重载这些函数,在函数被调用时记录调用栈或其他信息。

步骤:

  1. 编写一个 C 文件,实现你要重载的函数:

    #define _GNU_SOURCE
    #include <dlfcn.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <execinfo.h>
    
    typedef int (*orig_open_f_type)(const char *pathname, int flags);
    
    int open(const char *pathname, int flags) {
        // 打印调用栈
        void *buffer[10];
        int nptrs = backtrace(buffer, 10);
        backtrace_symbols_fd(buffer, nptrs, STDERR_FILENO);
    
        // 调用原始的 open 函数
        orig_open_f_type orig_open;
        orig_open = (orig_open_f_type)dlsym(RTLD_NEXT, "open");
        return orig_open(pathname, flags);
    }
    
  2. 编译成共享库:

    gcc -shared -fPIC -o myopen.so myopen.c -ldl
    
  3. 使用 LD_PRELOAD 运行目标程序:

    LD_PRELOAD=./myopen.so ./your_program
    

    这样,每次 open 函数被调用时,你的自定义函数会被调用,并打印出调用栈。

gdb假设没有加载符号表 是否能断点一些特定函数呢?

在 GDB 中,如果程序没有加载符号表(例如,编译时未使用 -g 选项),调试器将无法识别函数名或变量名。这是因为符号表包含了可执行文件中函数的名称、变量的名称及其对应的内存地址等调试信息。没有符号表的情况下,GDB 只能看到程序的机器代码和内存地址,而无法直接断点到特定的函数名。

然而,即使没有符号表,仍然有一些方法可以对特定的函数或代码位置设置断点,具体方法取决于情况:

1. 使用内存地址设置断点

如果你知道目标函数的内存地址,你可以直接在该地址设置断点。即使没有符号表,GDB 仍然可以在指定的内存地址处暂停程序执行。

步骤:

  1. 启动 GDB 并加载程序:

    gdb ./your_program
    
  2. 运行程序或使用 start 命令让程序执行到某个位置。

  3. 如果你知道目标函数的地址(例如,通过反汇编工具或 nm 命令获取),你可以直接在该地址设置断点。例如:

    break *0x4005d0
    

    这里 0x4005d0 是目标函数的内存地址。* 表示在该地址处设置断点。

  4. 运行程序,GDB 会在该地址处暂停。

如何获取函数地址:

  • 你可以使用 nmobjdump 等工具来列出程序中的函数地址。例如:

    nm ./your_program | grep some_function
    

    如果存在 some_function,它会显示类似以下的输出:

    00000000004005d0 T some_function
    

    其中 0x4005d0some_function 的地址。

  • 另一种方法是使用 objdump 来反汇编可执行文件并找到目标函数:

    objdump -d ./your_program | less
    

    然后在反汇编输出中查找你感兴趣的函数地址。

2. 使用汇编指令设置断点

如果你知道某个函数的汇编指令或知道特定的汇编指令模式(如函数的入口处通常是 pushmov 指令),你可以通过反汇编来找到这个函数的地址,并在该地址设置断点。

步骤:

  1. 反汇编程序的入口点或特定代码段:

    disassemble main
    

    如果没有符号表,GDB 也可以反汇编出汇编指令(即使没有函数名),例如:

    disassemble 0x4005d0
    
  2. 找到感兴趣的汇编指令后,设置断点:

    break *0x4005d0
    

3. 使用 GDB 的自动分析功能

GDB 有一些自动分析功能,即使没有符号表,它也可以尝试自动获取某些函数的地址。例如,info functions 可以列出程序中找到的所有函数地址(尽管没有符号名)。

info functions

这将列出程序中的所有已知函数,即使没有符号表,它也可能找到一些与标准库函数相关的符号。

4. 使用共享库中函数的符号

如果你调试的程序动态链接了共享库,即使主程序没有符号表,GDB 仍然可以识别共享库中的符号。例如,如果程序使用了标准 C 库函数 printf,你可以使用以下命令为 printf 设置断点:

break printf

因为共享库通常会加载自己的符号表,因此 GDB 可以识别这些库中的函数。

5. 使用条件断点

如果你不知道函数的内存地址,也没有符号表,但你知道某个特定位置的条件(比如某个寄存器的值或内存内容),你可以使用条件断点来捕捉该条件。例如,如果你知道某个函数的返回地址或特定的寄存器值,可以这样设置断点:

break *0x4005d0 if $rax == 5

这表示在地址 0x4005d0 处设置断点,但仅当寄存器 rax 的值为 5 时才触发断点。

小结

  • 没有符号表:GDB 无法直接通过函数名设置断点,但你可以使用目标函数的内存地址来设置断点。
  • 获取地址:可以通过工具如 nm, objdump,或者使用 GDB 的反汇编功能来获取函数的入口地址。
  • 共享库函数:即使没有符号表,动态链接库中的符号仍然可以被识别,允许为标准库函数设置断点。

例如,通过命令 break *<address>,你可以在任何特定的内存地址处设置断点。

假如一个线程因为抛了一个异常终止了 如何能知道哪个函数抛异常呢?在gdb中

当一个线程因为抛出异常而终止时,你可以使用 GDB 来调试并确定哪个函数抛出了异常。GDB 提供了多种工具来捕获异常和检查堆栈,从而帮助你找出异常的来源。

步骤概述:

  1. 设置断点捕获异常抛出
  2. 使用回溯(backtrace)查看调用栈,找出哪个函数抛出了异常。

1. 在 GDB 中捕获异常抛出

GDB 提供了一个特殊的命令可以捕获 C++ 异常抛出。当有异常抛出时,程序会暂停,这样你就可以在抛出异常的时刻检查调用栈。

你可以使用以下命令捕获所有 C++ 异常的抛出:

catch throw

这个命令会告诉 GDB,在异常被抛出的时候暂停程序的执行。

捕获异常的具体步骤:

  1. 启动 GDB,并加载你的程序:

    gdb ./your_program
    
  2. 设置捕捉异常的断点:

    catch throw
    
  3. 运行程序:

    run
    
  4. 当程序抛出异常时,GDB 会暂停,并显示类似以下的信息:

    Catchpoint 1 (throw)
    

2. 查看调用栈(Backtrace)

当程序由于抛出异常而暂停时,你可以使用 backtrace(或简写 bt)命令查看调用栈,找出哪个函数抛出了异常。

backtrace

这将显示当前的调用栈,列出从最底层到最顶层的函数调用顺序。通过这个调用栈,你可以找到抛出异常的函数。

示例:

假设你运行了程序并且捕捉到了异常抛出:

(gdb) catch throw
Catchpoint 1 (throw)
(gdb) run
Starting program: ./your_program
Catchpoint 1 (throw), __cxa_throw () at /build/glibc-23423....

现在你可以使用 backtrace 来查看调用栈:

(gdb) backtrace
#0  __cxa_throw () at /build/glibc-23423....
#1  0x00000000004015ae in some_function() at main.cpp:10
#2  0x0000000000401234 in main () at main.cpp:20

如上所示,some_function() 函数在 main.cpp 第 10 行抛出了异常。

3. 捕获异常处理开始(可选)

除了捕获异常的抛出,你还可以捕获异常的处理过程(即捕获 catch 处理异常的时刻),方法是使用:

catch catch

这会在异常被捕获处理的时候暂停程序。如果你想调试异常处理过程,这个命令非常有用。

4. 其他有用的 GDB 命令

  • info threads:查看所有线程的状态。如果你调试的是多线程程序,可以使用这个命令来查看线程情况。
  • thread apply all bt:如果程序有多个线程,这个命令可以显示所有线程的调用栈,帮助你确定哪个线程抛出了异常。

5. 使用调试符号

为了确保你能够看到函数名称、行号等详细信息,建议在编译程序时启用调试符号(使用 -g 选项)。例如:

g++ -g -o your_program your_program.cpp

如果没有调试符号,GDB 可能只会显示内存地址,而不会显示函数名称和代码行号。

6. 通过反汇编查看异常(无调试符号时)

如果你调试的程序没有调试符号,调用栈可能只会显示内存地址而不是函数名。在这种情况下,你可以使用 GDB 的 disassemble 命令来查看抛出异常的汇编代码,并手动确定抛出异常的位置。

例如:

disassemble 0x4005d0

这会显示内存地址 0x4005d0 处的汇编指令。你可以通过反汇编和代码分析,推测哪个函数抛出了异常。

总结

  1. 捕获异常:使用 GDB 的 catch throw 命令捕获所有 C++ 异常。
  2. 查看调用栈:使用 backtrace 命令查看调用栈,找到抛出异常的函数。
  3. 多线程支持:如果你的程序是多线程的,使用 info threadsthread apply all bt 来查看各线程的状态和调用栈。
  4. 调试符号:确保程序在编译时加入了调试符号(-g),这将帮助你在 GDB 中看到更多的调试信息。

通过这些步骤,你应该能够在 GDB 中轻松找到哪个函数抛出了异常,即使是在多线程环境下。

我在第二个栈帧 我能拿到什么上下文呢?

小结

当你进入某个特定的栈帧(例如第二个栈帧)时,能够获取的上下文信息包括:

  • 局部变量和函数参数:使用 info localsinfo args
  • 调用栈:使用 backtracebt 查看调用栈的上下文。
  • 源代码:使用 listframe 查看当前帧对应的源代码。
  • 寄存器状态:使用 info registers 查看寄存器的值。
  • 内存内容:使用 x 命令查看任意内存地址的内容。
  • 全局变量:使用 printinfo variables 查看全局变量的值。
  • 线程信息:使用 info threads 查看当前线程的状态。
  • 返回地址:使用 info frame 查看当前帧的返回地址。
  • 动态类型:使用 printptype 查看 C++ 对象的动态类型。

如何检索一个so里 是否定义了某种符号呢?

要检索一个共享库(.so 文件)中是否定义了某个符号(函数、变量等),你可以使用以下几种工具:

1. nm 命令

nm 是一个常用的工具,用来列出对象文件中的符号。你可以使用它来查看 .so 文件中定义的符号。

使用方法:

nm -D <your_library.so> | grep <symbol_name>
  • -D:表示显示动态符号(即只显示共享库中的符号)。
  • grep <symbol_name>:用于过滤你想要查找的符号。

示例:

nm -D libexample.so | grep my_function

如果库中定义了 my_function,则会显示类似以下的输出:

0000000000001234 T my_function

其中:

  • T 表示该符号在库中定义(T 表示符号在代码段中)。
  • U 表示该符号是未定义的(即该共享库引用了该符号,但并未定义它)。
  • 其他符号类型也存在(如 B 表示 BSS 段中的符号,D 表示数据段中的符号等)。

2. readelf 命令

readelf 是另一个工具,可以显示 ELF 文件的详细信息(ELF 是 Linux 和其他类 Unix 系统中常用的可执行文件格式)。

使用方法:

readelf -Ws <your_library.so> | grep <symbol_name>
  • -W:表示不折行输出,显示完整的符号信息。
  • -s:表示显示符号表。

示例:

readelf -Ws libexample.so | grep my_function

同样,如果库中定义了 my_function,你会看到类似下面的输出:

   Num:    Value          Size Type    Bind   Vis      Ndx Name
    33: 0000000000012345   100 FUNC    GLOBAL DEFAULT   12 my_function
  • FUNC 表示这是一个函数。
  • GLOBAL 表示这是一个全局符号。
  • DEFAULT 表示默认可见性。

3. objdump 命令

objdump 是另一个用于分析二进制文件的工具,也可以列出共享库中的符号。

使用方法:

objdump -T <your_library.so> | grep <symbol_name>
  • -T:表示显示动态符号表。

示例:

objdump -T libexample.so | grep my_function

输出类似于:

0000000000012345 g    DF .text  00000000000000a4  Base        my_function
  • g 表示全局符号。
  • DF 表示这是一个函数符号。
  • .text 表示函数位于代码段。

4. lddldconfig

如果你不确定是否包含在某个共享库中,lddldconfig 可以帮助你查看共享库的依赖关系,确认共享库实际被加载。

例如,使用 ldd 可以列出可执行文件所依赖的共享库:

ldd <your_executable>

这不会直接列出符号,但可以帮助你确认共享库是否被正确加载。

5. objdump + grep 自动化查找符号(可选)

你可以通过简单地结合 objdumpgrep 来自动化查找符号:

for lib in $(ldconfig -p | awk '{print $4}'); do
    echo "Checking $lib"
    objdump -T $lib | grep my_function
done

这将遍历系统中的所有共享库,检查它们是否包含 my_function 符号。

总结

要查找一个 .so 文件中是否定义了某个符号,你可以使用以下工具:

  • nm -D <your_library.so> | grep <symbol_name>:列出动态符号。
  • readelf -Ws <your_library.so> | grep <symbol_name>:查看 ELF 文件的符号表。
  • objdump -T <your_library.so> | grep <symbol_name>:显示动态符号表。

这些工具可以帮助你快速检索某个共享库是否定义了特定的符号。

标签:调用,函数,查看,gdb,线程,断点
From: https://www.cnblogs.com/jaqennnnn/p/18449364

相关文章

  • Debuggers 1012:Introductory GDB
    OpenSecurityTraining2LearningPaths:VulnerabilityHunting&Exploitationpython:https://www.learnpython.org/路径图:https://ost2.fyi/OST2_LP_Vulns_Exploits.pdfDebuggers1012:IntroductoryGDB(与python)-->Architecture1001:x86-64Assembly-->R......
  • linux gdb debuging
    GDBGNU下的一个调试软件,在linux下可以用来调试c/c++代码。启动可以通过gdb--help查看用法,如下:ThisistheGNUdebugger.Usage:gdb[options][executable-file[core-fileorprocess-id]]gdb[options]--argsexecutable-file[inferior-arguments...]gdb[optio......
  • mongdb7副本集修改IP
    mongdb7副本集修改IP环境信息主机名IP端口资源mongodb1192.168.56.111C/2G/50Gmongodb2192.168.56.121C/2G/50Gmongodb3192.168.56.131C/2G/50Goperatingsystem:CentOS-7.6-x86_64-DVD-1810.isoMasterIP:192.168.56.11SlaveIP:192.168.56.12ArbiterIP:192.168.56.13......
  • 通过jlink连接树莓派4b搭建gdb调试环境
    参考资料jlink使用的是博光微的jlinkv9树莓派使用的是4bjlink与树莓派链接按照表中的内容进行链接,注意链接过程要小心仔细,否则后续排查起来十分麻烦下载openocd由于我是在linux环境下,所以省去了jlink的驱动安装,直接先下载openocdsudoaptinstallopenocd注意不要去......
  • 存算分离+双集群容灾丨云和恩墨与华为共同发布 MogDB × OceanStor Dorado 联合解决方
    引言为期三天的第九届华为全联接大会(HUAWEICONNECT2024)于9月19日在上海世博中心&展览馆盛大召开。本次大会以“共赢行业智能化”为主题,邀请思想领袖、商业精英、技术专家、合作伙伴、开发者等业界同仁,从战略、产业、生态等方面探讨如何通过智能化、数字化技术,赋能千行万业,把握新......
  • [20240920]跟踪library cache lock library cache pin使用gdb.txt
    [20240920]跟踪librarycachelocklibrarycachepin使用gdb.txt--//前一阵子,写的使用gdb跟踪librarycachelocklibrarycachepin的脚本有一个小问题,无法获得lockaddress以及pinaddress--//地址,有一点点小缺陷,尝试修改完善看看。--//按照https://nenadnoveljic.com/blog/tr......
  • 在链接与运行地址不同时gdb的调试方法
    搭建一个链接和运行不同的环境SECTIONS{ .=0xffff000000080000, /*.=0x80000,*/ .text.boot:{*(.text.boot)} .text:{*(.text)} .rodata:{*(.rodata)} .....}-s还可以看到符号都链接到高地址去了但是elf文件中有详细的地址信息,如果后续qemu加载......
  • 如何处理monogdb的慢查询
    识别慢查询有几种方法可以用来查看mongodb中的慢查询。1.使用profiler如何使用profiler,需要参考之前的笔记。 清空profiler内容:>usemydb;>db.system.profile.remove();2.使用db.currentOp()此方法是实时分析。db.currentOp(true)db.currentOp({"active":tru......
  • GDB
    GDB常用命令GDB中有许多常用命令,以下是一些常见的命令:1.启动调试启动GDB:gdb<可执行文件>附加到运行中的进程:如果程序已经在运行中,你可以用以下命令来附加到某个正在运行的进程:gdb<可执行文件><进程ID>2.设置断点设置断点:在指定的函数名或行号上设置断......
  • 在ubuntu中安装较新版本的gcc和gdb
    要在Ubuntu中安装较新版本的GCC和GDB,可以通过以下几种方法实现。Ubuntu的默认包管理器apt提供的软件包通常不是最新的,因此需要使用PPA或手动编译以获取较新版本。方法1:使用ppa:ubuntu-toolchain-r/test安装最新版本的GCC和GDBUbuntuToolchainPPA提供了更新的......