Docker搭建i386实验环境,gcc多态实现

简介: 使用Docker搭建了i386的实验环境,并且使用多种工具验证了gcc实现多态的机制。

Docker设置

首先选择ubuntu 20.04镜像,按照docker官网指引,安装docker。

之后编写 Dockerfile ,如下

FROM scratch
ADD ubuntu-bionic-oci-i386-root.tar.gz /
WORKDIR /
COPY rootfs /
RUN apt update
RUN apt install -y openssh-server psmisc net-tools

由于host系统已经是64位ubuntu了,所以直接 FROM scratch ,之后将从![ubuntu oci image](https://partner-images.canonical.com/oci/)获取得到的32位ubuntu rootfs拷贝到容器中的根目录。之后将项目目录中 rootfs 拷贝到容器的根目录,覆盖sources.list、覆盖ssh-key、root账号的authorized_key。

之后编写 docker-compose.yml 如下,将ssh的22端口映射到host机器的5022,设置启动运行脚本,防止栈地址随机化。

version: "3"services:  i386:    container_name: i386
    image: i386
    build:      context: DockerBuild/i386
      dockerfile: Dockerfile
    command: /bin/bash /docker-entrypoint.d/run.sh
    ports:      - "5022:22"    volumes:      - ./DockerBuild/i386:/docker-entrypoint.d
# GDB    security_opt:      - seccomp:unconfined
    cap_add:      - SYS_PTRACE

测试代码

主要测试C++中虚函数表的实现方法,基本的测试代码如下:

classPlainPerson{
public:
constchar*get_job() {
return"none";
    }
};
classPerson {
public:
virtualvoidplaceholder() {};
virtualconstchar*get_job() {
return"none";
    }
};
classStudent: publicPerson {
public:
virtualconstchar*get_job() {
return"student";
    }
};
voidprint_job(Person*p){
printf("job for 0x%X is '%s'\n", p, p->get_job());
}
intmain() {
printf("sizeof(void)         = %d\n", sizeof(void));
printf("sizeof(PlainPersono) = %d\n", sizeof(PlainPerson));
printf("sizeof(Persono)      = %d\n", sizeof(Person));
Person*p0=newPerson();
Person*p1=newStudent();
print_job(p0);
print_job(p1);
return0;
}

实验结果

使用 g++ -fno-pic -no-pie main.cpp 编译代码,使用 objdump -Mintel -d a.out 查看反汇编。

08048546 <_Z9print_jobP6Person>:
 8048546:       55                      push   ebp
 8048547:       89 e5                   mov    ebp,esp
 8048549:       83 ec 08                sub    esp,0x8
 804854c:       8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 804854f:       8b 00                   mov    eax,DWORD PTR [eax]
 8048551:       83 c0 04                add    eax,0x4
 8048554:       8b 00                   mov    eax,DWORD PTR [eax]
 8048556:       83 ec 0c                sub    esp,0xc
 8048559:       ff 75 08                push   DWORD PTR [ebp+0x8]
 804855c:       ff d0                   call   eax
 804855e:       83 c4 10                add    esp,0x10
 8048561:       83 ec 04                sub    esp,0x4
 8048564:       50                      push   eax
 8048565:       ff 75 08                push   DWORD PTR [ebp+0x8]
 8048568:       68 0d 87 04 08          push   0x804870d
 804856d:       e8 9e fe ff ff          call   8048410 <printf@plt>
 8048572:       83 c4 10                add    esp,0x10
 8048575:       90                      nop
 8048576:       c9                      leave
 8048577:       c3                      ret

函数里面有两个 call ,应该分别对应 get_jobprintf 。首先第一个 call 的地址来自寄存器 eax,除去调整栈大小和传参操作,在 call 之前只剩下4如下指令:

mov    eax,DWORD PTR [ebp+0x8]   // 获取第一个参数,即 Person* p;
mov    eax,DWORD PTR [eax]       // f* f_arr = *(f**)(p);
add    eax,0x4                   
mov    eax,DWORD PTR [eax]       // f func = f_arr[1];

说明编译器将实现多态的虚函数表指针(即 f**),放到了对象的头4字节中。

由于 virtual void placeholder() 的影响,获取 get_name 的实际位置需要访问虚函数表中的第二项(第一项留给了来自父类的placeholder函数)。

使用gdb验证我们的猜想,如下所示:

(gdb) b *0x804855c
Breakpoint 1 at 0x804855c
(gdb) c
The program is not being run.
(gdb) run
Starting program: /root/test/run 
sizeof(void)         = 1
sizeof(PlainPersono) = 1
sizeof(Persono)      = 4
Breakpoint 1, 0x0804855c in print_job(Person*) ()
(gdb) i r eax
eax            0x804863a  134514234
(gdb) x $eax
0x804863a <_ZN6Person7get_jobEv>: 0xb8e58955

根据反编译结果打断点,之后使用 x 命令查看 eax 寄存器对应内存区域的内容,显示是一个函数。查看反汇编可以确认此函数就是实际调用的虚函数。

总结

GCC将虚函数表的指针放到对象的前4个字节,在调用时候,从虚函数表中获取真实的函数指针。这种设计只要保证父类和子类的虚函数表对应位置保持相同语义即可。

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
1月前
|
数据采集 存储 Docker
深入理解Docker:为你的爬虫项目提供隔离环境
本教程介绍如何使用Docker构建隔离环境,运行Python爬虫项目,采集小红书视频页面的简介和评论。主要内容包括: 1. **Docker隔离环境**:通过Docker容器化爬虫,确保环境独立、易于部署。 2. **代理IP技术**:利用亿牛云爬虫代理突破反爬限制。 3. **Cookie与User-Agent设置**:伪装请求头,模拟真实用户访问。 4. **多线程采集**:提高数据采集效率。 前置知识要求:Python基础、Docker基本操作及HTML解析(可选)。教程还涵盖常见错误解决方法和延伸练习,帮助你优化爬虫代码并避免陷阱。
深入理解Docker:为你的爬虫项目提供隔离环境
|
3月前
|
负载均衡 网络协议 算法
Docker容器环境中服务发现与负载均衡的技术与方法,涵盖环境变量、DNS、集中式服务发现系统等方式
本文探讨了Docker容器环境中服务发现与负载均衡的技术与方法,涵盖环境变量、DNS、集中式服务发现系统等方式,以及软件负载均衡器、云服务负载均衡、容器编排工具等实现手段,强调两者结合的重要性及面临挑战的应对措施。
148 3
|
4月前
|
关系型数据库 MySQL Docker
docker环境下mysql镜像启动后权限更改问题的解决
在Docker环境下运行MySQL容器时,权限问题是一个常见的困扰。通过正确设置目录和文件的权限,可以确保MySQL容器顺利启动并正常运行。本文提供了多种解决方案,包括在主机上设置正确的权限、使用Dockerfile和Docker Compose进行配置、在容器启动后手动更改权限以及使用 `init`脚本自动更改权限。根据实际情况选择合适的方法,可以有效解决MySQL容器启动后的权限问题。希望本文对您在Docker环境下运行MySQL容器有所帮助。
847 1
|
5月前
|
网络安全 虚拟化 Docker
SSH后判断当前服务器是云主机、物理机、虚拟机、docker环境
结合上述方法,您可以对当前环境进行较为准确的判断。重要的是理解每种环境的特征,并通过系统的响应进行综合分析。如果在Docker容器内,通常会有明显的环境标志和受限的资源视图;而在云主机或虚拟机上,虽然它们也可能是虚拟化的,但通常提供更接近物理机的体验,且可通过硬件标识来识别虚拟化平台。物理机则直接反映硬件真实信息,较少有虚拟化痕迹。通过这些线索,您应该能够定位到您所处的环境类型。
143 2
|
5月前
|
存储 监控 Shell
docker的底层原理二:容器运行时环境
本文深入探讨了Docker容器运行时环境的关键技术,包括命名空间、控制组、联合文件系统、容器运行时以及分离的进程树,这些技术共同确保了容器的隔离性、资源控制和可移植性。
82 5
|
5月前
|
jenkins Java 持续交付
Docker搭建jenkins环境
这篇文章详细介绍了如何利用Docker搭建Jenkins环境,包括拉取Jenkins镜像、配置端口映射及初始化设置的步骤。
302 0
Docker搭建jenkins环境
|
6月前
|
虚拟化 Docker Windows
window 10专业版部署docker环境
本文介绍了如何在Windows 10专业版上部署Docker环境,包括安装步骤、配置镜像加速以及可能遇到的错误处理。
257 3
window 10专业版部署docker环境
|
6月前
|
NoSQL 关系型数据库 Redis
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo
mall在linux环境下的部署(基于Docker容器),docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongodb、minio详细教程,拉取镜像、运行容器
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo
|
5月前
|
Linux 持续交付 iOS开发
docker的使用(环境特殊时可以考虑使用)
docker的使用(环境特殊时可以考虑使用)
32 0
|
6月前
|
关系型数据库 数据库 网络虚拟化
Docker环境下重启PostgreSQL数据库服务的全面指南与代码示例
由于时间和空间限制,我将在后续的回答中分别涉及到“Python中采用lasso、SCAD、LARS技术分析棒球运动员薪资的案例集锦”以及“Docker环境下重启PostgreSQL数据库服务的全面指南与代码示例”。如果你有任何一个问题的优先顺序或需要立即回答的,请告知。
109 0