设为首页 加入收藏

TOP

修改,编译,GDB调试openjdk8源码(docker环境下)(一)
2019-09-17 15:18:07 】 浏览:48
Tags:修改 编译 GDB 调试 openjdk8 源码 docker 环境

在上一章《在docker上编译openjdk8》里,我们在docker容器内成功编译了openjdk8的源码,有没有读者朋友产生过这个念头:“能不能修改openjdk源码,构建一个与众不同的jdk“,今天我们就来阅读一些openjdk的源码,再尝试做些小改动并验证。

我们先编译openjdk:
首先通过命令git clone git@github.com:zq2599/centos7_build_openjdk8.git下载构建镜像所需的文件,下载后打开控制台进入centos7_build_openjdk8目录,执行

docker build -t bolingcavalryopenjdk:0.0.1 .

这样就构建好了镜像文件,再执行启动docker容器的命令(命令中的参数“–security-opt seccomp=unconfined”有特殊用处,稍后会讲到):

docker run --name=jdk001 --security-opt seccomp=unconfined -idt bolingcavalryopenjdk:0.0.1

然后执行以下命令进入容器的控制台:

docker exec -it jdk001 /bin/bash

进入容器的控制台后执行以下两个命令开始编译:

./configure --with-debug-level=slowdebug
make all ZIP_DEBUGINFO_FILES=0 DISABLE_HOTSPOT_OS_VERSION_CHECK=OK CONF=linux-x86_64-normal-server-slowdebug

以上就是编译openjdk的步骤了,请大家开始编译吧,因为等会儿会用到,我们要用编译好的jdk做调试。

现在开始看源码吧,本次分析的目标是针对我们熟悉的java -version命令,当我们在终端敲下这个命令的时候,jvm到底做了些什么呢?

整个分析验证的流程是这样的:

这里写图片描述

准备工作:
在容器内通过vim看源码是很不方便的,所以我这里是在电脑上复制了一份openjdk的源码(下载地址:http://www.java.net/download/openjdk/jdk8/promoted/b132/openjdk-8-src-b132-03_mar_2014.zip
),用sublime text3打开openjdk源码,真正到了要修改的时候再去docker容器里通过vi修改。

寻找程序入口

第一步就是把程序的入口和源码对应起来,先要找到入口main函数,步骤如下:

  1. 在docker容器内的/usr/local/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/bin目录下,执行命令以下命令可以进入GDB的命令行模式:
gdb --args ./java -version

效果如下图,可以看到已进入GDB命令行模式,可以继续输入GDB命令了:

这里写图片描述

输入b main命令,在main函数打断点,此时GDB会返回断点位置的信息,如下图,main函数的位置在/usr/local/openjdk/jdk/src/share/bin/main.c, line 97:

这里写图片描述

再输入l命令可以打印源码,如下图:

这里写图片描述

在容器外的电脑上,通过sublime text3或者其他ide打开main.c,如下图,开始读代码吧:

这里写图片描述

顺序阅读代码

main函数中的代码并不多,但有几个宏定义会扰乱我们思路,从字面上看#ifdef _WIN32这样的宏应该是windows平台下才会生效的,但总不能每次都靠字面推断,此时打断点单步执行是最直接的方法,但是在打断点之前,我们先解决前面遗留的一个问题吧,此问题挺重要的

还记得我们启动docker容器的命令么:

docker run --name=jdk001 --security-opt seccomp=unconfined -idt bolingcavalryopenjdk:0.0.1

命令中的–security-opt seccomp=unconfined参数有什么用?为何要留在打断点之前再次提到这个参数?

这个参数和Docker的安全机制有关,具体的文档链接在这里,请读者们自行参悟,本人的英文太差就不献丑了,简单的说就是Docker有个Seccomp filtering功能,以伯克莱封包过滤器(Berkeley Packet Filter,缩写BPF)的方式允许用户对容器内的系统调用(syscall)做自定义的“allow”, “deny”, “trap”, “kill”, or “trace”操作,由于Seccomp filtering的限制,在默认的配置下,会导致我们在用GDB的时候run失败,所以在执行docker run的时候加入–security-opt seccomp=unconfined这个参数,可以关闭seccomp profile的功能;

我之前不知道seccomp profile的限制,用命令docker run –name=jdk001 -idt bolingcavalryopenjdk:0.0.1启动了容器,编译可以成功,但是在用GDB调试的时候出了问题,如下图:

这里写图片描述

上图中,黄框中的“进入GDB”和“b main”(添加断点)两个命令都能正常执行,但是红框中的”r”(运行程序)命令在执行的时候提示错误“Error disabling address space randomization: Operation not permitted”,在执行”n”(单步执行)命令的时候提示程序不在运行中。

遗留问题已经澄清,可以继续跟踪代码了,之前我们已经在GDB输入了”b mian”,给main函数打了断点,现在输入”r”开始执行,然后就会看到main函数的断点已经生效,输入”n”可以跟踪代码执行到了哪一行,如下图:

这里写图片描述

原来代码执行的位置分别是97,122,123,125这四行,和下图的源码完全对应上了:

这里写图片描述

有了GDB神器,可以愉快的阅读源码了:

main.c的main函数中,调用JLI_Launch函数,在Sublime text3中,将鼠标放置在”JLI_Launch”位置,会弹出一个小窗口,上面是JLI_Launch函数的声明和定义的两个链接,如下图:

这里写图片描述

点击第一个链接,跳转到JLI_Launch函数的定义位置:

//根据环境变量初始化debug标志位,后续的日志是否会打印靠这个debug标志控制了
    InitLauncher(javaw);
    //如果设置了debug,就会打印一些辅助信息 
    DumpState();     
    if (JLI_IsTraceLauncher()) {
        int i;
        printf("Command line args:\n");
        for (i = 0; i < argc ; i++) {
            printf("argv[%d] = %s\n", i, argv[i]);
        }
        AddOption("-Dsun.java.launcher.diag=true", NULL);
    }  //如果设置debug标志位,就打印命令行参数,并加入额外参数

    //选择jre版本,在jar包的manifest文件或者命令行中都可以对jr
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇工作中遇到的99%SQL优化,这里都.. 下一篇《从零开始学架构》笔记——第二..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目