add dockerfile and update docs
This commit is contained in:
parent
72e3b9b512
commit
fab216f6bd
|
@ -0,0 +1,53 @@
|
||||||
|
FROM ubuntu:20.04
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
# install required packages
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y cmake \
|
||||||
|
clang \
|
||||||
|
automake \
|
||||||
|
autoconf \
|
||||||
|
autotools-dev \
|
||||||
|
libtool \
|
||||||
|
python \
|
||||||
|
g++ \
|
||||||
|
git \
|
||||||
|
wget \
|
||||||
|
pkg-config \
|
||||||
|
cpio \
|
||||||
|
grub2 \
|
||||||
|
xorriso
|
||||||
|
|
||||||
|
# install elf tools
|
||||||
|
COPY ./scripts/read_procmgr_elf_tool /home/read_procmgr_elf_tool
|
||||||
|
WORKDIR /home/read_procmgr_elf_tool
|
||||||
|
RUN gcc elf.c main.c -o read_procmgr_elf_tool && \
|
||||||
|
mkdir -p /usr/bin/ && \
|
||||||
|
cp read_procmgr_elf_tool /usr/bin/
|
||||||
|
|
||||||
|
# copy user dir
|
||||||
|
COPY ./user /chos/user
|
||||||
|
|
||||||
|
# install musl-cross-make
|
||||||
|
WORKDIR /home
|
||||||
|
RUN git clone https://ghproxy.com//https://github.com/richfelker/musl-cross-make.git
|
||||||
|
WORKDIR /home/musl-cross-make
|
||||||
|
COPY ./scripts/musl-cross-make/config.mak .
|
||||||
|
RUN sed -i 's|MUSL_SRCDIR = $(REL_TOP)/musl-$(MUSL_VER)|MUSL_SRCDIR = /chos/user/musl-1.1.24|' Makefile && \
|
||||||
|
export C_INCLUDE_PATH="/chos/user/sys-include" && \
|
||||||
|
make -j12 && \
|
||||||
|
make install -j12
|
||||||
|
|
||||||
|
# install libevent (for memcached)
|
||||||
|
WORKDIR /home
|
||||||
|
RUN git clone https://ghproxy.com//https://github.com/libevent/libevent.git && \
|
||||||
|
cd libevent && \
|
||||||
|
./autogen.sh && \
|
||||||
|
./configure --disable-openssl --disable-debug-mode --disable-samples -prefix=/usr/libevent && \
|
||||||
|
make CC=/chos/user/musl-1.1.24/build/bin/musl-gcc -s && \
|
||||||
|
make install
|
||||||
|
|
||||||
|
# install hiredis (for YCSB-C)
|
||||||
|
WORKDIR /chos/user/demos/YCSB-C/redis/hiredis
|
||||||
|
RUN make CC=/chos/user/musl-1.1.24/build/bin/musl-gcc -s && \
|
||||||
|
make install
|
20
README.md
20
README.md
|
@ -26,7 +26,7 @@ or use
|
||||||
./chbuild build
|
./chbuild build
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run in QEMU
|
### Run in QEMU
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./qemu.exp # with a clean NVM backend
|
./qemu.exp # with a clean NVM backend
|
||||||
|
@ -35,9 +35,21 @@ or use
|
||||||
or
|
or
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./build/simulate.sh
|
./build/simulate.sh # with the old NVM backend
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
By default, we provide a pre-built Docker image, you can simply use the build command and it will be automatically downloaded.
|
||||||
|
|
||||||
|
If you want to build this image from scratch, you can use the following command to build from the provided dockerfile.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker build -t <image_name> .
|
||||||
|
```
|
||||||
|
|
||||||
|
To use the newly built container, you can modify the Docker image name in the `chbuild` file (specifically, line 218 in the `_docker_run()` function) to the image you have built.
|
||||||
|
|
||||||
## Artificial Evaluation
|
## Artificial Evaluation
|
||||||
|
|
||||||
Please refer to [artificial_eval.md](./artificial_eval.md)
|
Please refer to [artificial_eval.md](./artificial_eval.md)
|
||||||
|
@ -65,6 +77,10 @@ Please refer to [artificial_eval.md](./artificial_eval.md)
|
||||||
|- config.cmake user applications flags
|
|- config.cmake user applications flags
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### TreeSLS's Implementation
|
||||||
|
|
||||||
|
Please refer to [TreeSLS.md](./docs/TreeSLS.md)
|
||||||
|
|
||||||
## LICENSE
|
## LICENSE
|
||||||
|
|
||||||
LICENSE of ported applications are given in subdirs.
|
LICENSE of ported applications are given in subdirs.
|
||||||
|
|
|
@ -6,11 +6,15 @@ We thank the artifact evaluators who have volunteered to do one of the toughest
|
||||||
|
|
||||||
Hardware
|
Hardware
|
||||||
- Intel® Optane™ Persistent Memory (or you can use Qemu mode for simulation)
|
- Intel® Optane™ Persistent Memory (or you can use Qemu mode for simulation)
|
||||||
|
- CPU Core >= 20
|
||||||
|
|
||||||
Software
|
Software
|
||||||
- docker: we build the OS within a given docker
|
- docker: we build the OS within a given docker
|
||||||
|
- qemu-system-x86: use qemu mode to boot the OS
|
||||||
- ipmitool: for interacting with the real machine (with kernel loaded)
|
- ipmitool: for interacting with the real machine (with kernel loaded)
|
||||||
- expect & python3: for scripts
|
- expect: for interacting with the real machine
|
||||||
|
- python3: for parsing and drawing
|
||||||
|
- requirements: matplotlib, pandas, numpy, seaborn
|
||||||
|
|
||||||
## Building TreeSLS OS
|
## Building TreeSLS OS
|
||||||
|
|
||||||
|
@ -208,3 +212,25 @@ After all, run `./fig12.sh`.
|
||||||
2. run Rocksdb test provided by Aurora (https://github.com/rcslab/aurora-bench/tree/master), scripts are given in `6-aurora-rocksdb/test_rockdb.sh`
|
2. run Rocksdb test provided by Aurora (https://github.com/rcslab/aurora-bench/tree/master), scripts are given in `6-aurora-rocksdb/test_rockdb.sh`
|
||||||
3. copy logs of Aurora (by default, in `/aurora-data/`) to `artificial_evaluation/logs/<mode>/rocksdb/`
|
3. copy logs of Aurora (by default, in `/aurora-data/`) to `artificial_evaluation/logs/<mode>/rocksdb/`
|
||||||
3. run `./fig14.sh`
|
3. run `./fig14.sh`
|
||||||
|
|
||||||
|
## Common Q&A
|
||||||
|
Q. Changing the CPU number
|
||||||
|
|
||||||
|
A. Currenly we hard code the CPU number. To change it, you can:
|
||||||
|
- Open the file `kernel/arch/x86_64/boot/CMakeLists.txt`. Change line 36 `-smp XXX` with a new value.
|
||||||
|
- Open the file `/kernel/include/arch/x86_64/plat/intel/machine.h`. Change line 4 `#define PLAT_CPU_NUM XXX` to half the value used above. For example, if using `-smp 20`, change it to `#define PLAT_CPU_NUM 10`. (we use half of the cores to use a single NUMA on the server).
|
||||||
|
|
||||||
|
Q. QEMU bug:
|
||||||
|
|
||||||
|
```
|
||||||
|
[INFO] General Protection Fault
|
||||||
|
[INFO] Faulting Address: 0x0
|
||||||
|
[INFO] Current thread 0
|
||||||
|
[INFO] Trap from 0xffffffffc011338b EC 0 Trap No. 13
|
||||||
|
[INFO] DS 0x0, CS 0x10, RSP 0xffffffffc0109030, SS 0x18
|
||||||
|
[INFO] rax: 0x706b0, rdx: 0x80010031, rdi: 0xffffffffca14aa80
|
||||||
|
[INFO] rcx: 0x65
|
||||||
|
```
|
||||||
|
|
||||||
|
A. We have also encountered this bug on Ubuntu 22.04. The issue stems from QEMU having problems with emulating the CPU's PCID feature support on this particular version, thus leading to a General Protection Fault bug.
|
||||||
|
You can try running it on a machine with a different operating system installed. We have successfully boot it on some machines with Debian and Fedora.
|
5
chbuild
5
chbuild
|
@ -211,8 +211,9 @@ _docker_run() {
|
||||||
$@
|
$@
|
||||||
else
|
else
|
||||||
test -t 1 && use_tty="-t"
|
test -t 1 && use_tty="-t"
|
||||||
docker run -i $use_tty --rm \
|
# docker run -i $use_tty --rm \
|
||||||
-u $(id -u ${USER}):$(id -g ${USER}) \
|
# -u $(id -u ${USER}):$(id -g ${USER}) \
|
||||||
|
docker run -it \
|
||||||
-v $(pwd):/chos -w /chos \
|
-v $(pwd):/chos -w /chos \
|
||||||
promisivia/treesls_chcore_builder:v2.2 \
|
promisivia/treesls_chcore_builder:v2.2 \
|
||||||
$self $@
|
$self $@
|
||||||
|
|
634
chpm
634
chpm
|
@ -1,634 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
#
|
|
||||||
# 预设的环境变量:
|
|
||||||
#
|
|
||||||
# CHPM_TARGET_ARCH 目标编译架构
|
|
||||||
# CHPM_TARGET_PREFIX 目标安装位置
|
|
||||||
#
|
|
||||||
# CHPM_RECIPE_FILE recipe 文件路径 (ports/<package>/recipe.json)
|
|
||||||
# CHPM_RECIPE_DIR recipe 文件目录路径 (ports/<package>)
|
|
||||||
#
|
|
||||||
# CHPM_PKG_NAME 正在安装的包名
|
|
||||||
# CHPM_PKG_VERSION 正在安装的包版本
|
|
||||||
#
|
|
||||||
# CHPM_BUILD_DIR 临时构建目录 (.chpm/build/<package>)
|
|
||||||
#
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import zipfile
|
|
||||||
|
|
||||||
from os import path
|
|
||||||
from pprint import pprint
|
|
||||||
from functools import partial
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from argparse import ArgumentParser, Namespace
|
|
||||||
from typing import List, Set, Dict, Optional, Any, Callable
|
|
||||||
|
|
||||||
|
|
||||||
print_dbg = partial(print, file=sys.stdout)
|
|
||||||
print_info = partial(print, file=sys.stdout)
|
|
||||||
print_err = partial(print, file=sys.stderr)
|
|
||||||
|
|
||||||
|
|
||||||
CHCORE_DIR = os.getcwd()
|
|
||||||
CHPM_DIR = path.join(CHCORE_DIR, ".chpm")
|
|
||||||
PORTS_DIR = path.join(CHPM_DIR, "ports")
|
|
||||||
PORTS_REPO = "git@ipads.se.sjtu.edu.cn:ipads-os/chcore-ports.git"
|
|
||||||
BUILD_DIR = path.join(CHPM_DIR, "build")
|
|
||||||
DOWNLOADS_DIR = path.join(CHPM_DIR, "downloads")
|
|
||||||
DEFAULT_INSTALL_DIR = path.join(CHPM_DIR, "install")
|
|
||||||
PKGINFO_DIR_NAME = "chpm-pkginfo"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class File:
|
|
||||||
url: str
|
|
||||||
name: str
|
|
||||||
extract_dir: str
|
|
||||||
# sha256: Optional[str] = None # TODO
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class RecipeHead:
|
|
||||||
name: str
|
|
||||||
version: str
|
|
||||||
description: str
|
|
||||||
|
|
||||||
|
|
||||||
def read_recipe_head(package_name: str) -> Optional[RecipeHead]:
|
|
||||||
recipe_file = path.join(PORTS_DIR, package_name, "recipe.json")
|
|
||||||
if not path.isfile(recipe_file):
|
|
||||||
return None
|
|
||||||
|
|
||||||
with open(recipe_file, "r", encoding="utf-8") as f:
|
|
||||||
recipe_json = json.load(f)
|
|
||||||
|
|
||||||
return RecipeHead(
|
|
||||||
name=recipe_json["name"],
|
|
||||||
version=recipe_json["version"],
|
|
||||||
description=recipe_json["description"],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Recipe:
|
|
||||||
file: str
|
|
||||||
target_arch: str
|
|
||||||
name: str
|
|
||||||
version: str
|
|
||||||
description: str
|
|
||||||
supported_archs: Set[str] = field(default_factory=set)
|
|
||||||
dependencies: List["Recipe"] = field(default_factory=list)
|
|
||||||
source_files: List[File] = field(default_factory=list)
|
|
||||||
prebuilt_files: List[File] = field(default_factory=list)
|
|
||||||
docker_builder: Optional[str] = None
|
|
||||||
working_dir: str = "."
|
|
||||||
build: List[str] = field(default_factory=list)
|
|
||||||
install: List[str] = field(default_factory=list)
|
|
||||||
install_prebuilt: List[str] = field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
def read_recipe(package_name: str, arch: str) -> Recipe:
|
|
||||||
recipe_file = path.join(PORTS_DIR, package_name, "recipe.json")
|
|
||||||
if not path.isfile(recipe_file):
|
|
||||||
print_err(f"Package `{package_name}` not found")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
with open(recipe_file, "r", encoding="utf-8") as f:
|
|
||||||
recipe_json = json.load(f)
|
|
||||||
|
|
||||||
recipe = Recipe(
|
|
||||||
file=recipe_file,
|
|
||||||
target_arch=arch,
|
|
||||||
name=recipe_json["name"],
|
|
||||||
version=recipe_json["version"],
|
|
||||||
description=recipe_json["description"],
|
|
||||||
)
|
|
||||||
|
|
||||||
if "supported_archs" in recipe_json:
|
|
||||||
recipe.supported_archs = set(recipe_json["supported_archs"])
|
|
||||||
|
|
||||||
def get_arch_aware_list(
|
|
||||||
recipe_json: Dict[str, Any], key: str, arch: str
|
|
||||||
) -> List[Any]:
|
|
||||||
val = recipe_json.get(key)
|
|
||||||
if not val or not isinstance(val, (list, dict)):
|
|
||||||
return []
|
|
||||||
if isinstance(val, dict):
|
|
||||||
return val.get(arch, [])
|
|
||||||
return val
|
|
||||||
|
|
||||||
dependencies = get_arch_aware_list(recipe_json, "dependencies", arch)
|
|
||||||
for dep in dependencies:
|
|
||||||
recipe.dependencies.append(read_recipe(dep, arch))
|
|
||||||
|
|
||||||
def json_to_file(file_json: Dict[str, Any]) -> File:
|
|
||||||
return File(
|
|
||||||
url=file_json["url"],
|
|
||||||
name=file_json["name"],
|
|
||||||
extract_dir=file_json["extract_dir"],
|
|
||||||
)
|
|
||||||
|
|
||||||
source_files = get_arch_aware_list(recipe_json, "source_files", arch)
|
|
||||||
for file in source_files:
|
|
||||||
recipe.source_files.append(json_to_file(file))
|
|
||||||
prebuilt_files = get_arch_aware_list(recipe_json, "prebuilt_files", arch)
|
|
||||||
for file in prebuilt_files:
|
|
||||||
recipe.prebuilt_files.append(json_to_file(file))
|
|
||||||
|
|
||||||
recipe.docker_builder = recipe_json.get("docker_builder")
|
|
||||||
recipe.working_dir = recipe_json.get("working_dir") or "$CHPM_BUILD_DIR"
|
|
||||||
recipe.build = get_arch_aware_list(recipe_json, "build", arch)
|
|
||||||
recipe.install = get_arch_aware_list(recipe_json, "install", arch)
|
|
||||||
recipe.install_prebuilt = get_arch_aware_list(recipe_json, "install_prebuilt", arch)
|
|
||||||
return recipe
|
|
||||||
|
|
||||||
|
|
||||||
def check_recipe_installable(recipe: Recipe):
|
|
||||||
unsupported_packages = set()
|
|
||||||
|
|
||||||
def _intersect_supported_archs(r: Recipe, arch: str):
|
|
||||||
if r.supported_archs and arch not in r.supported_archs:
|
|
||||||
unsupported_packages.add(r.name)
|
|
||||||
for dep in r.dependencies:
|
|
||||||
_intersect_supported_archs(dep, arch)
|
|
||||||
|
|
||||||
_intersect_supported_archs(recipe, recipe.target_arch)
|
|
||||||
if unsupported_packages:
|
|
||||||
pkgs = ", ".join([f"`{x}`" for x in sorted(list(unsupported_packages))])
|
|
||||||
be = "is" if len(unsupported_packages) == 1 else "are"
|
|
||||||
print_err(
|
|
||||||
f"Package {pkgs} {be} not supported to install on `{recipe.target_arch}`"
|
|
||||||
)
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PkgInfo:
|
|
||||||
name: str
|
|
||||||
version: str
|
|
||||||
description: str
|
|
||||||
dependencies: List["str"]
|
|
||||||
is_dependency: bool
|
|
||||||
# installed_files: List[str] # TODO
|
|
||||||
|
|
||||||
|
|
||||||
def read_pkginfo(recipe: Recipe, prefix: str) -> Optional[PkgInfo]:
|
|
||||||
pkginfo_file = path.join(prefix, PKGINFO_DIR_NAME, f"{recipe.name}.json")
|
|
||||||
if not path.exists(pkginfo_file):
|
|
||||||
return None
|
|
||||||
|
|
||||||
with open(pkginfo_file, "r", encoding="utf-8") as f:
|
|
||||||
pkginfo_json = json.load(f)
|
|
||||||
return PkgInfo(**pkginfo_json)
|
|
||||||
|
|
||||||
|
|
||||||
def read_all_pkginfos(prefix: str) -> List[PkgInfo]:
|
|
||||||
pkginfo_dir = path.join(prefix, PKGINFO_DIR_NAME)
|
|
||||||
if not path.exists(pkginfo_dir):
|
|
||||||
return []
|
|
||||||
|
|
||||||
pkginfos = []
|
|
||||||
for file in os.listdir(pkginfo_dir):
|
|
||||||
with open(path.join(pkginfo_dir, file), "r", encoding="utf-8") as f:
|
|
||||||
pkginfo_json = json.load(f)
|
|
||||||
pkginfos.append(PkgInfo(**pkginfo_json))
|
|
||||||
return pkginfos
|
|
||||||
|
|
||||||
|
|
||||||
def write_pkginfo(recipe: Recipe, is_dependency: bool, prefix: str):
|
|
||||||
pkginfo_dir = path.join(prefix, PKGINFO_DIR_NAME)
|
|
||||||
pkginfo_file = path.join(pkginfo_dir, f"{recipe.name}.json")
|
|
||||||
os.makedirs(pkginfo_dir, exist_ok=True)
|
|
||||||
deps = [d.name for d in recipe.dependencies]
|
|
||||||
pkginfo = PkgInfo(
|
|
||||||
name=recipe.name,
|
|
||||||
version=recipe.version,
|
|
||||||
description=recipe.description,
|
|
||||||
dependencies=deps,
|
|
||||||
is_dependency=is_dependency,
|
|
||||||
)
|
|
||||||
with open(pkginfo_file, "w", encoding="utf-8") as f:
|
|
||||||
json.dump(pkginfo.__dict__, f, indent=4)
|
|
||||||
|
|
||||||
|
|
||||||
def download_file(file: File, downloads_dir: str) -> Optional[str]:
|
|
||||||
local_path = path.join(downloads_dir, file.name)
|
|
||||||
print_dbg("local_path", local_path)
|
|
||||||
if path.exists(local_path):
|
|
||||||
print_dbg(f"File `{local_path}` already exists")
|
|
||||||
else:
|
|
||||||
if subprocess.call(["curl", "-L", "-o", local_path, "--fail", file.url]) != 0:
|
|
||||||
if path.isfile(local_path):
|
|
||||||
os.remove(local_path)
|
|
||||||
return None
|
|
||||||
return local_path
|
|
||||||
|
|
||||||
|
|
||||||
def extract_file(file: File, local_file: str, build_dir: str) -> Optional[str]:
|
|
||||||
extract_dir = path.join(build_dir, file.extract_dir)
|
|
||||||
print_dbg("extract_dir:", extract_dir)
|
|
||||||
if path.exists(extract_dir):
|
|
||||||
print_dbg(f"Directory `{extract_dir}` already exists")
|
|
||||||
return extract_dir
|
|
||||||
|
|
||||||
tmp_extract_dir = path.join(build_dir, file.extract_dir + "_tmp")
|
|
||||||
print_dbg("tmp_extract_dir:", tmp_extract_dir)
|
|
||||||
os.mkdir(tmp_extract_dir)
|
|
||||||
|
|
||||||
class ZipFileWithPermissions(zipfile.ZipFile):
|
|
||||||
def _extract_member(self, member, targetpath, pwd):
|
|
||||||
if not isinstance(member, zipfile.ZipInfo):
|
|
||||||
member = self.getinfo(member)
|
|
||||||
|
|
||||||
super_extract_member = getattr(super(), "_extract_member")
|
|
||||||
targetpath = super_extract_member(member, targetpath, pwd)
|
|
||||||
|
|
||||||
attr = member.external_attr >> 16
|
|
||||||
if attr != 0:
|
|
||||||
os.chmod(targetpath, attr)
|
|
||||||
return targetpath
|
|
||||||
|
|
||||||
def unpack_zip(filename: str, extract_dir: str):
|
|
||||||
with ZipFileWithPermissions(filename) as zip:
|
|
||||||
zip.extractall(extract_dir)
|
|
||||||
|
|
||||||
shutil.unregister_unpack_format("zip")
|
|
||||||
shutil.register_unpack_format("zip", [".zip"], unpack_zip)
|
|
||||||
try:
|
|
||||||
shutil.unpack_archive(local_file, tmp_extract_dir)
|
|
||||||
except Exception:
|
|
||||||
shutil.rmtree(tmp_extract_dir)
|
|
||||||
return None
|
|
||||||
|
|
||||||
# strip the only subdirectory if any
|
|
||||||
subdirs = os.listdir(tmp_extract_dir)
|
|
||||||
if len(subdirs) == 1 and subdirs[0] == file.extract_dir:
|
|
||||||
shutil.move(path.join(tmp_extract_dir, subdirs[0]), extract_dir)
|
|
||||||
shutil.rmtree(tmp_extract_dir)
|
|
||||||
else:
|
|
||||||
shutil.move(tmp_extract_dir, extract_dir)
|
|
||||||
return extract_dir
|
|
||||||
|
|
||||||
|
|
||||||
def download_and_extract_files(
|
|
||||||
files: List[File], downloads_dir: str, build_dir: str
|
|
||||||
) -> bool:
|
|
||||||
for file in files:
|
|
||||||
print_info(f"Downloading {file.name} from {file.url}...")
|
|
||||||
local_file = download_file(file, downloads_dir)
|
|
||||||
if not local_file:
|
|
||||||
print_err("Failed to download", file.name, "from", file.url)
|
|
||||||
return False
|
|
||||||
print_info(f"Extracting {file.name} to {file.extract_dir}...")
|
|
||||||
extract_dir = extract_file(file, local_file, build_dir)
|
|
||||||
if not extract_dir:
|
|
||||||
print_err(
|
|
||||||
"Failed to extract",
|
|
||||||
file.name,
|
|
||||||
"to",
|
|
||||||
file.extract_dir,
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def eval_env_vars(s: str) -> str:
|
|
||||||
return subprocess.check_output(["bash", "-c", f'echo -n "{s}"']).decode()
|
|
||||||
|
|
||||||
|
|
||||||
def try_install_recipe_from_prebuilt(
|
|
||||||
recipe: Recipe,
|
|
||||||
downloads_dir: str,
|
|
||||||
build_dir: str,
|
|
||||||
cmd_prefix: List[str],
|
|
||||||
working_dir: str,
|
|
||||||
):
|
|
||||||
if not download_and_extract_files(recipe.prebuilt_files, downloads_dir, build_dir):
|
|
||||||
return False
|
|
||||||
|
|
||||||
print_info("Installing from prebuilt files...")
|
|
||||||
for install_cmd in recipe.install_prebuilt:
|
|
||||||
cmd = cmd_prefix + ["bash", "-c", f"cd {working_dir} && " + install_cmd]
|
|
||||||
print_dbg("cmd:", cmd)
|
|
||||||
ret = subprocess.call(cmd)
|
|
||||||
if ret != 0:
|
|
||||||
print_err("Failed to run install prebuilt command")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def try_install_recipe_from_source(
|
|
||||||
recipe: Recipe,
|
|
||||||
downloads_dir: str,
|
|
||||||
build_dir: str,
|
|
||||||
cmd_prefix: List[str],
|
|
||||||
working_dir: str,
|
|
||||||
) -> bool:
|
|
||||||
if not download_and_extract_files(recipe.source_files, downloads_dir, build_dir):
|
|
||||||
return False
|
|
||||||
|
|
||||||
print_info("Installing from source files...")
|
|
||||||
|
|
||||||
print_info("Running build commands...")
|
|
||||||
for build_cmd in recipe.build:
|
|
||||||
cmd = cmd_prefix + ["bash", "-c", f"cd {working_dir} && " + build_cmd]
|
|
||||||
print_dbg("cmd:", cmd)
|
|
||||||
ret = subprocess.call(cmd)
|
|
||||||
if ret != 0:
|
|
||||||
print_err("Failed to run build command")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print_info("Running install commands...")
|
|
||||||
for install_cmd in recipe.install:
|
|
||||||
cmd = cmd_prefix + ["bash", "-c", f"cd {working_dir} && " + install_cmd]
|
|
||||||
print_dbg("cmd:", cmd)
|
|
||||||
ret = subprocess.call(cmd)
|
|
||||||
if ret != 0:
|
|
||||||
print_err("Failed to run install command")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def try_install_recipe(recipe: Recipe, downloads_dir: str, build_dir: str) -> bool:
|
|
||||||
os.environ["CHPM_RECIPE_FILE"] = recipe.file
|
|
||||||
os.environ["CHPM_RECIPE_DIR"] = path.dirname(recipe.file)
|
|
||||||
os.environ["CHPM_PKG_NAME"] = recipe.name
|
|
||||||
os.environ["CHPM_PKG_VERSION"] = recipe.version
|
|
||||||
os.environ["CHPM_BUILD_DIR"] = build_dir
|
|
||||||
|
|
||||||
for file in recipe.source_files + recipe.prebuilt_files:
|
|
||||||
file.url = eval_env_vars(file.url)
|
|
||||||
file.name = eval_env_vars(file.name)
|
|
||||||
file.extract_dir = eval_env_vars(file.extract_dir)
|
|
||||||
recipe.working_dir = eval_env_vars(recipe.working_dir)
|
|
||||||
|
|
||||||
cmd_prefix = []
|
|
||||||
if recipe.docker_builder:
|
|
||||||
# For packages that should be built in docker container,
|
|
||||||
# we prepare a prefix for every build command and install
|
|
||||||
# command.
|
|
||||||
cmd_prefix.extend(
|
|
||||||
[
|
|
||||||
"docker",
|
|
||||||
"run",
|
|
||||||
"-ti",
|
|
||||||
"--rm",
|
|
||||||
"-u",
|
|
||||||
f"{os.getuid()}:{os.getgid()}",
|
|
||||||
"-v",
|
|
||||||
f"{CHCORE_DIR}:{CHCORE_DIR}",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
for (env, val) in os.environ.items():
|
|
||||||
if env.startswith("CHPM_"):
|
|
||||||
cmd_prefix.extend(["-e", f"{env}={val}"])
|
|
||||||
cmd_prefix.append(recipe.docker_builder)
|
|
||||||
|
|
||||||
working_dir = recipe.working_dir
|
|
||||||
print_dbg("working_dir:", recipe.working_dir)
|
|
||||||
|
|
||||||
if recipe.install_prebuilt:
|
|
||||||
return try_install_recipe_from_prebuilt(
|
|
||||||
recipe, downloads_dir, build_dir, cmd_prefix, working_dir
|
|
||||||
)
|
|
||||||
return try_install_recipe_from_source(
|
|
||||||
recipe, downloads_dir, build_dir, cmd_prefix, working_dir
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def install_recipe(
|
|
||||||
recipe: Recipe, prefix: str, is_dependency: bool = False, dev_mode: bool = False
|
|
||||||
):
|
|
||||||
if read_pkginfo(recipe, prefix):
|
|
||||||
print_info(f"`{recipe.name}` is already installed")
|
|
||||||
return
|
|
||||||
|
|
||||||
for dep in recipe.dependencies:
|
|
||||||
install_recipe(dep, prefix, is_dependency=True)
|
|
||||||
|
|
||||||
print_info(f"Installing `{recipe.name}`...")
|
|
||||||
|
|
||||||
downloads_dir = path.join(DOWNLOADS_DIR, recipe.target_arch, recipe.name)
|
|
||||||
os.makedirs(downloads_dir, exist_ok=True)
|
|
||||||
print_dbg("downloads_dir:", downloads_dir)
|
|
||||||
|
|
||||||
build_dir = path.join(BUILD_DIR, recipe.name)
|
|
||||||
if not dev_mode and path.isdir(build_dir):
|
|
||||||
shutil.rmtree(build_dir)
|
|
||||||
os.makedirs(build_dir, exist_ok=dev_mode)
|
|
||||||
print_dbg("build_dir:", build_dir)
|
|
||||||
|
|
||||||
ok = try_install_recipe(recipe, downloads_dir, build_dir)
|
|
||||||
if not dev_mode:
|
|
||||||
shutil.rmtree(build_dir)
|
|
||||||
|
|
||||||
if ok:
|
|
||||||
write_pkginfo(recipe, is_dependency, prefix)
|
|
||||||
print_info(f"Successfully installed `{recipe.name}`")
|
|
||||||
else:
|
|
||||||
print_err(f"Failed to install `{recipe.name}`")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
subcommands = {}
|
|
||||||
|
|
||||||
|
|
||||||
def subcommand(name: str):
|
|
||||||
def deco(func: Callable) -> Callable:
|
|
||||||
subcommands[name] = func
|
|
||||||
return func
|
|
||||||
|
|
||||||
return deco
|
|
||||||
|
|
||||||
|
|
||||||
@subcommand("init")
|
|
||||||
def subcommand_init(args: Namespace):
|
|
||||||
if path.exists(CHPM_DIR):
|
|
||||||
if args.force:
|
|
||||||
shutil.rmtree(CHPM_DIR)
|
|
||||||
else:
|
|
||||||
print_err(
|
|
||||||
"`.chpm` directory already exists, "
|
|
||||||
"add `--force` option to force re-initialization"
|
|
||||||
)
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
print_info("Initializing `.chpm` directory...")
|
|
||||||
os.makedirs(DOWNLOADS_DIR, exist_ok=True)
|
|
||||||
os.makedirs(BUILD_DIR, exist_ok=True)
|
|
||||||
print_info(f"Cloning `ports` repo ({PORTS_REPO})...")
|
|
||||||
ret = subprocess.call(["git", "clone", "--depth=1", PORTS_REPO, PORTS_DIR])
|
|
||||||
if ret != 0:
|
|
||||||
print_err(f"Failed to clone `ports` repo")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
print_info(f"Successfully initialized")
|
|
||||||
|
|
||||||
|
|
||||||
@subcommand("update")
|
|
||||||
def subcommand_update(args: Namespace):
|
|
||||||
print_info("Updating `ports` repo...")
|
|
||||||
ret = subprocess.call(["git", "-C", PORTS_DIR, "pull", "origin", "master"])
|
|
||||||
if ret != 0:
|
|
||||||
print_err(f"Failed to update `ports` repo")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
print_info(f"Successfully updated")
|
|
||||||
|
|
||||||
|
|
||||||
@subcommand("install")
|
|
||||||
def subcommand_install(args: Namespace):
|
|
||||||
# subcommand_update(args)
|
|
||||||
print() # for a new line
|
|
||||||
print_info(f"Installing packages into `{args.prefix}`...\n")
|
|
||||||
|
|
||||||
arch = args.arch
|
|
||||||
prefix = path.abspath(args.prefix)
|
|
||||||
os.environ["CHPM_TARGET_ARCH"] = arch
|
|
||||||
os.environ["CHPM_TARGET_PREFIX"] = prefix
|
|
||||||
|
|
||||||
for package_name in args.packages:
|
|
||||||
recipe = read_recipe(package_name, arch)
|
|
||||||
print_dbg("recipe:", recipe)
|
|
||||||
check_recipe_installable(recipe)
|
|
||||||
install_recipe(recipe, args.prefix, dev_mode=args.dev_mode)
|
|
||||||
|
|
||||||
|
|
||||||
@subcommand("list")
|
|
||||||
def subcommand_list(args: Namespace):
|
|
||||||
print_info(f"Listing packages installed into `{args.prefix}`...\n")
|
|
||||||
|
|
||||||
prefix = path.abspath(args.prefix)
|
|
||||||
pkginfos = read_all_pkginfos(prefix)
|
|
||||||
if pkginfos:
|
|
||||||
print_info("Installed packages:\n")
|
|
||||||
for pkginfo in pkginfos:
|
|
||||||
print_info(f"{pkginfo.name}={pkginfo.version}")
|
|
||||||
else:
|
|
||||||
print_info("No packages installed\n")
|
|
||||||
|
|
||||||
|
|
||||||
@subcommand("config")
|
|
||||||
def subcommand_config(args: Namespace):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@subcommand("search")
|
|
||||||
def subcommand_search(args: Namespace):
|
|
||||||
subcommand_update(args)
|
|
||||||
print() # for a new line
|
|
||||||
|
|
||||||
keyword = args.keyword
|
|
||||||
print_dbg("search keyword:", keyword)
|
|
||||||
|
|
||||||
ports = os.listdir(PORTS_DIR)
|
|
||||||
result = []
|
|
||||||
for port in ports:
|
|
||||||
print_dbg("port:", port)
|
|
||||||
if keyword in port:
|
|
||||||
result.append(port)
|
|
||||||
print_dbg("search result:", result)
|
|
||||||
|
|
||||||
if result:
|
|
||||||
print_info("Search result:\n")
|
|
||||||
for pkg in result:
|
|
||||||
recipe_head = read_recipe_head(pkg)
|
|
||||||
assert recipe_head
|
|
||||||
print_info(
|
|
||||||
f"{recipe_head.name} ({recipe_head.version}):\n\t{recipe_head.description}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def create_argparser() -> ArgumentParser:
|
|
||||||
parser = ArgumentParser(description="The ChCore Package Manager.")
|
|
||||||
parser.add_argument(
|
|
||||||
"-v",
|
|
||||||
"--verbose",
|
|
||||||
dest="verbose",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help="Verbose mode",
|
|
||||||
)
|
|
||||||
subparsers = parser.add_subparsers(dest="subcommand")
|
|
||||||
|
|
||||||
parser_init = subparsers.add_parser("init", help="initialize `.chpm` directory")
|
|
||||||
parser_init.add_argument(
|
|
||||||
"-f",
|
|
||||||
"--force",
|
|
||||||
dest="force",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help="remove existing `.chpm` dir",
|
|
||||||
)
|
|
||||||
|
|
||||||
subparsers.add_parser("update", help="update `ports` repo")
|
|
||||||
|
|
||||||
parser_install = subparsers.add_parser("install", help="install package")
|
|
||||||
parser_install.add_argument(
|
|
||||||
"-p",
|
|
||||||
"--prefix",
|
|
||||||
dest="prefix",
|
|
||||||
default=DEFAULT_INSTALL_DIR,
|
|
||||||
help="prefix of install destination",
|
|
||||||
)
|
|
||||||
parser_install.add_argument(
|
|
||||||
"-a", "--arch", dest="arch", required=True, help="target architecture"
|
|
||||||
)
|
|
||||||
parser_install.add_argument(
|
|
||||||
"-d",
|
|
||||||
"--dev-mode",
|
|
||||||
dest="dev_mode",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help="enable development mode (temporary build directory won't be deleted)",
|
|
||||||
)
|
|
||||||
parser_install.add_argument("packages", nargs="+", help="packages to install")
|
|
||||||
|
|
||||||
parser_list = subparsers.add_parser("list", help="list installed package")
|
|
||||||
parser_list.add_argument(
|
|
||||||
"-p",
|
|
||||||
"--prefix",
|
|
||||||
dest="prefix",
|
|
||||||
default=DEFAULT_INSTALL_DIR,
|
|
||||||
help="prefix of install destination",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser_config = subparsers.add_parser("config", help="change config")
|
|
||||||
|
|
||||||
parser_search = subparsers.add_parser("search", help="search for packages")
|
|
||||||
parser_search.add_argument("keyword", help="search keyword")
|
|
||||||
|
|
||||||
# TODO: other subcommand
|
|
||||||
|
|
||||||
return parser
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = create_argparser()
|
|
||||||
args = parser.parse_args()
|
|
||||||
if not args.subcommand:
|
|
||||||
parser.print_help()
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
global print_dbg
|
|
||||||
if not args.verbose:
|
|
||||||
print_dbg = lambda *a, **kw: None
|
|
||||||
|
|
||||||
print_dbg("Command args:", args)
|
|
||||||
|
|
||||||
if not path.exists(CHPM_DIR) and args.subcommand != "init":
|
|
||||||
print_err("Please run `./chpm init` first")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
subcommands[args.subcommand](args)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Implementation of TreeSLS
|
||||||
|
|
||||||
|
We implemented TreeSLS based on ChCore, which is an educational multicore microkernel that supports POSIX APIs through musl-libc.
|
||||||
|
|
||||||
|
TreeSLS's modification includes:
|
||||||
|
- checkpoint/restore module (`kernel/ckpt`)
|
||||||
|
- memory allocator module (`kernel/mm`)
|
||||||
|
- ipi module (`kernel/ipi`)
|
||||||
|
- several new syscalls (`kernel/syscall`)
|
||||||
|
- user applications (listed in `user/sample-apps/apps/treesls`)
|
||||||
|
|
||||||
|
## Checkpoint/Restore
|
||||||
|
|
||||||
|
TreeSLS adds a checkpoint/restore module (`kernel/ckpt`).
|
||||||
|
Whole-system checkpoint/restore can be taken through `sys_whole_ckpt` (`kernel/ckpt/ckpt.c`) and `sys_whole_restore` (`kernel/ckpt/restore.c`).
|
||||||
|
|
||||||
|
Hybrid page checkpointing is done through `process_sub_active_list()`, which is called in parallel to the main process of checkpointing.
|
||||||
|
|
||||||
|
## Memory Allocator
|
||||||
|
|
||||||
|
TreeSLS modifies the memory allocator (`kernel/mm`) by adding a lightweight journal when malloc and free pages from the buddy system.
|
||||||
|
The journal is taken through `prepare_latest_log()` in every malloc/free function.
|
||||||
|
|
||||||
|
## Other Modules (IPI, syscall, etc.)
|
||||||
|
|
||||||
|
TreeSLS also modified some other modules (IPI, syscall, etc.) to enable checkpointing.
|
||||||
|
|
||||||
|
1. IPI: add `sys_ipi_stop_all` and `sys_ipi_start_all`.
|
||||||
|
2. syscall: export `sys_whole_ckpt`, `sys_whole_restore`, and several supporting syscalls.
|
|
@ -1,12 +1,12 @@
|
||||||
# SLS Basic Configruations
|
# SLS Basic Configruations
|
||||||
set(SLS_RESTORE ON)
|
set(SLS_RESTORE OFF)
|
||||||
set(SLS_EXT_SYNC OFF)
|
set(SLS_EXT_SYNC OFF)
|
||||||
set(SLS_HYBRID_MEM ON)
|
set(SLS_HYBRID_MEM ON)
|
||||||
|
|
||||||
# SLS Report Details
|
# SLS Report Details
|
||||||
set(SLS_REPORT_CKPT ON)
|
set(SLS_REPORT_CKPT OFF)
|
||||||
set(SLS_REPORT_RESTORE ON)
|
set(SLS_REPORT_RESTORE OFF)
|
||||||
set(SLS_REPORT_HYBRID ON)
|
set(SLS_REPORT_HYBRID OFF)
|
||||||
|
|
||||||
# SLS special tests: for tests of only-checkpoint, +pf, +memcpy
|
# SLS special tests: for tests of only-checkpoint, +pf, +memcpy
|
||||||
set(SLS_SPECIAL_OMIT_PF OFF)
|
set(SLS_SPECIAL_OMIT_PF OFF)
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
#
|
||||||
|
# config.mak.dist - sample musl-cross-make configuration
|
||||||
|
#
|
||||||
|
# Copy to config.mak and edit as desired.
|
||||||
|
#
|
||||||
|
|
||||||
|
# There is no default TARGET; you must select one here or on the make
|
||||||
|
# command line. Some examples:
|
||||||
|
|
||||||
|
# TARGET = i486-linux-musl
|
||||||
|
TARGET = x86_64-linux-musl
|
||||||
|
# TARGET = arm-linux-musleabi
|
||||||
|
# TARGET = arm-linux-musleabihf
|
||||||
|
# TARGET = sh2eb-linux-muslfdpic
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# By default, cross compilers are installed to ./output under the top-level
|
||||||
|
# musl-cross-make directory and can later be moved wherever you want them.
|
||||||
|
# To install directly to a specific location, set it here. Multiple targets
|
||||||
|
# can safely be installed in the same location. Some examples:
|
||||||
|
|
||||||
|
OUTPUT = /home/musl-cross-make/install
|
||||||
|
# OUTPUT = /opt/cross
|
||||||
|
# OUTPUT = /usr/local
|
||||||
|
|
||||||
|
# By default, latest supported release versions of musl and the toolchain
|
||||||
|
# components are used. You can override those here, but the version selected
|
||||||
|
# must be supported (under hashes/ and patches/) to work. For musl, you
|
||||||
|
# can use "git-refname" (e.g. git-master) instead of a release. Setting a
|
||||||
|
# blank version for gmp, mpc, mpfr and isl will suppress download and
|
||||||
|
# in-tree build of these libraries and instead depend on pre-installed
|
||||||
|
# libraries when available (isl is optional and not set by default).
|
||||||
|
# Setting a blank version for linux will suppress installation of kernel
|
||||||
|
# headers, which are not needed unless compiling programs that use them.
|
||||||
|
|
||||||
|
# BINUTILS_VER = 2.25.1
|
||||||
|
# GCC_VER = 5.2.0
|
||||||
|
# MUSL_VER = git-master
|
||||||
|
# GMP_VER =
|
||||||
|
# MPC_VER =
|
||||||
|
# MPFR_VER =
|
||||||
|
# ISL_VER =
|
||||||
|
# LINUX_VER =
|
||||||
|
|
||||||
|
# By default source archives are downloaded with wget. curl is also an option.
|
||||||
|
|
||||||
|
# DL_CMD = wget -c -O
|
||||||
|
# DL_CMD = curl -C - -L -o
|
||||||
|
|
||||||
|
# Check sha-1 hashes of downloaded source archives. On gnu systems this is
|
||||||
|
# usually done with sha1sum.
|
||||||
|
|
||||||
|
# SHA1_CMD = sha1sum -c
|
||||||
|
# SHA1_CMD = sha1 -c
|
||||||
|
# SHA1_CMD = shasum -a 1 -c
|
||||||
|
|
||||||
|
# Something like the following can be used to produce a static-linked
|
||||||
|
# toolchain that's deployable to any system with matching arch, using
|
||||||
|
# an existing musl-targeted cross compiler. This only works if the
|
||||||
|
# system you build on can natively (or via binfmt_misc and qemu) run
|
||||||
|
# binaries produced by the existing toolchain (in this example, i486).
|
||||||
|
|
||||||
|
# COMMON_CONFIG += CC="i486-linux-musl-gcc -static --static" CXX="i486-linux-musl-g++ -static --static"
|
||||||
|
|
||||||
|
# Recommended options for smaller build for deploying binaries:
|
||||||
|
|
||||||
|
# COMMON_CONFIG += CFLAGS="-g0 -Os" CXXFLAGS="-g0 -Os" LDFLAGS="-s"
|
||||||
|
|
||||||
|
# Options you can add for faster/simpler build at the expense of features:
|
||||||
|
|
||||||
|
# COMMON_CONFIG += --disable-nls
|
||||||
|
# GCC_CONFIG += --disable-libquadmath --disable-decimal-float
|
||||||
|
# GCC_CONFIG += --disable-libitm
|
||||||
|
# GCC_CONFIG += --disable-fixed-point
|
||||||
|
# GCC_CONFIG += --disable-lto
|
||||||
|
# GCC_CONFIG += --enable-default-pie
|
||||||
|
|
||||||
|
# By default C and C++ are the only languages enabled, and these are
|
||||||
|
# the only ones tested and known to be supported. You can uncomment the
|
||||||
|
# following and add other languages if you want to try getting them to
|
||||||
|
# work too.
|
||||||
|
|
||||||
|
# GCC_CONFIG += --enable-languages=c,c++
|
||||||
|
|
||||||
|
# You can keep the local build path out of your toolchain binaries and
|
||||||
|
# target libraries with the following, but then gdb needs to be told
|
||||||
|
# where to look for source files.
|
||||||
|
|
||||||
|
# COMMON_CONFIG += --with-debug-prefix-map=$(CURDIR)=
|
|
@ -0,0 +1,323 @@
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <endian.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "elf.h"
|
||||||
|
|
||||||
|
#define PAGE_SIZE 0x1000
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO(MK): Simplify the duplications in the file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool is_elf_magic(struct elf_indent *indent)
|
||||||
|
{
|
||||||
|
return (indent->ei_magic[0] == 0x7F &&
|
||||||
|
indent->ei_magic[1] == 'E' &&
|
||||||
|
indent->ei_magic[2] == 'L' &&
|
||||||
|
indent->ei_magic[3] == 'F');
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ELF_ENDIAN_LE(indent) ((indent).ei_data == 1)
|
||||||
|
#define ELF_ENDIAN_BE(indent) ((indent).ei_data == 2)
|
||||||
|
|
||||||
|
#define ELF_BITS_32(indent) ((indent).ei_class == 1)
|
||||||
|
#define ELF_BITS_64(indent) ((indent).ei_class == 2)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an ELF file header. We use the 64-bit structure `struct elf_header` as
|
||||||
|
* the output structure.
|
||||||
|
*
|
||||||
|
* On error, the negative error code is returned.
|
||||||
|
* On success, 0 is returned, and the header is written in the given parameter.
|
||||||
|
*/
|
||||||
|
static int parse_elf_header(const char *code, struct elf_header *header)
|
||||||
|
{
|
||||||
|
struct elf_header *header_64 = (struct elf_header *)code;
|
||||||
|
struct elf_header_32 *header_32 = (struct elf_header_32 *)code;
|
||||||
|
|
||||||
|
if (!is_elf_magic(&header_64->e_indent)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
header->e_indent = *(struct elf_indent *)code;
|
||||||
|
|
||||||
|
if (ELF_ENDIAN_LE(header->e_indent)) {
|
||||||
|
/*
|
||||||
|
* For the first few bytes, both 32-bit and 64-bit ELF headers
|
||||||
|
* have the same field width. So, we simply use header_64 at
|
||||||
|
* first.
|
||||||
|
*/
|
||||||
|
header->e_type = le16toh(header_64->e_type);
|
||||||
|
header->e_machine = le16toh(header_64->e_machine);
|
||||||
|
header->e_version = le32toh(header_32->e_version);
|
||||||
|
if (ELF_BITS_32(header->e_indent)) {
|
||||||
|
header->e_entry = le32toh(header_32->e_entry);
|
||||||
|
header->e_phoff = le32toh(header_32->e_phoff);
|
||||||
|
header->e_shoff = le32toh(header_32->e_shoff);
|
||||||
|
header->e_flags = le32toh(header_32->e_flags);
|
||||||
|
header->e_ehsize = le16toh(header_32->e_ehsize);
|
||||||
|
header->e_phentsize = le16toh(header_32->e_phentsize);
|
||||||
|
header->e_phnum = le16toh(header_32->e_phnum);
|
||||||
|
header->e_shentsize = le16toh(header_32->e_shentsize);
|
||||||
|
header->e_shnum = le16toh(header_32->e_shnum);
|
||||||
|
header->e_shstrndx = le16toh(header_32->e_shstrndx);
|
||||||
|
} else if (ELF_BITS_64(header->e_indent)) {
|
||||||
|
header->e_entry = le64toh(header_64->e_entry);
|
||||||
|
header->e_phoff = le64toh(header_64->e_phoff);
|
||||||
|
header->e_shoff = le64toh(header_64->e_shoff);
|
||||||
|
header->e_flags = le32toh(header_64->e_flags);
|
||||||
|
header->e_ehsize = le16toh(header_64->e_ehsize);
|
||||||
|
header->e_phentsize = le16toh(header_64->e_phentsize);
|
||||||
|
header->e_phnum = le16toh(header_64->e_phnum);
|
||||||
|
header->e_shentsize = le16toh(header_64->e_shentsize);
|
||||||
|
header->e_shnum = le16toh(header_64->e_shnum);
|
||||||
|
header->e_shstrndx = le16toh(header_64->e_shstrndx);
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else if (ELF_ENDIAN_BE(header->e_indent)) {
|
||||||
|
/*
|
||||||
|
* We use header_64 for the same reason as above.
|
||||||
|
*/
|
||||||
|
header->e_type = be16toh(header_64->e_type);
|
||||||
|
header->e_machine = be16toh(header_64->e_machine);
|
||||||
|
header->e_version = be32toh(header_32->e_version);
|
||||||
|
if (ELF_BITS_32(header->e_indent)) {
|
||||||
|
header->e_entry = be32toh(header_32->e_entry);
|
||||||
|
header->e_phoff = be32toh(header_32->e_phoff);
|
||||||
|
header->e_shoff = be32toh(header_32->e_shoff);
|
||||||
|
header->e_flags = be32toh(header_32->e_flags);
|
||||||
|
header->e_ehsize = be16toh(header_32->e_ehsize);
|
||||||
|
header->e_phentsize = be16toh(header_32->e_phentsize);
|
||||||
|
header->e_phnum = be16toh(header_32->e_phnum);
|
||||||
|
header->e_shentsize = be16toh(header_32->e_shentsize);
|
||||||
|
header->e_shnum = be16toh(header_32->e_shnum);
|
||||||
|
header->e_shstrndx = be16toh(header_32->e_shstrndx);
|
||||||
|
} else if (ELF_BITS_64(header->e_indent)) {
|
||||||
|
header->e_entry = be64toh(header_64->e_entry);
|
||||||
|
header->e_phoff = be64toh(header_64->e_phoff);
|
||||||
|
header->e_shoff = be64toh(header_64->e_shoff);
|
||||||
|
header->e_flags = be32toh(header_64->e_flags);
|
||||||
|
header->e_ehsize = be16toh(header_64->e_ehsize);
|
||||||
|
header->e_phentsize = be16toh(header_64->e_phentsize);
|
||||||
|
header->e_phnum = be16toh(header_64->e_phnum);
|
||||||
|
header->e_shentsize = be16toh(header_64->e_shentsize);
|
||||||
|
header->e_shnum = be16toh(header_64->e_shnum);
|
||||||
|
header->e_shstrndx = be16toh(header_64->e_shstrndx);
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an ELF program header. We use the 64-bit structure
|
||||||
|
* `struct elf_program_header` as the output structure.
|
||||||
|
*
|
||||||
|
* On error, the negative error code is returned.
|
||||||
|
* On success, 0 is returned, and the header is written in the given parameter.
|
||||||
|
*/
|
||||||
|
static int parse_elf_program_header(const char *code,
|
||||||
|
const struct elf_header *elf,
|
||||||
|
struct elf_program_header *header)
|
||||||
|
{
|
||||||
|
struct elf_program_header *header_64;
|
||||||
|
struct elf_program_header_32 *header_32;
|
||||||
|
|
||||||
|
if (ELF_ENDIAN_LE(elf->e_indent)) {
|
||||||
|
if (ELF_BITS_32(elf->e_indent)) {
|
||||||
|
header_32 = (struct elf_program_header_32 *)code;
|
||||||
|
header->p_type = le32toh(header_32->p_type);
|
||||||
|
header->p_flags = le32toh(header_32->p_flags);
|
||||||
|
header->p_offset = le32toh(header_32->p_offset);
|
||||||
|
header->p_vaddr = le32toh(header_32->p_vaddr);
|
||||||
|
header->p_paddr = le32toh(header_32->p_paddr);
|
||||||
|
header->p_filesz = le32toh(header_32->p_filesz);
|
||||||
|
header->p_memsz = le32toh(header_32->p_memsz);
|
||||||
|
header->p_align = le32toh(header_32->p_align);
|
||||||
|
} else if (ELF_BITS_64(elf->e_indent)) {
|
||||||
|
header_64 = (struct elf_program_header *)code;
|
||||||
|
header->p_type = le32toh(header_64->p_type);
|
||||||
|
header->p_flags = le32toh(header_64->p_flags);
|
||||||
|
header->p_offset = le64toh(header_64->p_offset);
|
||||||
|
header->p_vaddr = le64toh(header_64->p_vaddr);
|
||||||
|
header->p_paddr = le64toh(header_64->p_paddr);
|
||||||
|
header->p_filesz = le64toh(header_64->p_filesz);
|
||||||
|
header->p_memsz = le64toh(header_64->p_memsz);
|
||||||
|
header->p_align = le64toh(header_64->p_align);
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else if (ELF_ENDIAN_BE(elf->e_indent)) {
|
||||||
|
if (ELF_BITS_32(elf->e_indent)) {
|
||||||
|
header_32 = (struct elf_program_header_32 *)code;
|
||||||
|
header->p_type = be32toh(header_32->p_type);
|
||||||
|
header->p_flags = be32toh(header_32->p_flags);
|
||||||
|
header->p_offset = be32toh(header_32->p_offset);
|
||||||
|
header->p_vaddr = be32toh(header_32->p_vaddr);
|
||||||
|
header->p_paddr = be32toh(header_32->p_paddr);
|
||||||
|
header->p_filesz = be32toh(header_32->p_filesz);
|
||||||
|
header->p_memsz = be32toh(header_32->p_memsz);
|
||||||
|
header->p_align = be32toh(header_32->p_align);
|
||||||
|
} else if (ELF_BITS_64(elf->e_indent)) {
|
||||||
|
header_64 = (struct elf_program_header *)code;
|
||||||
|
header->p_type = be32toh(header_64->p_type);
|
||||||
|
header->p_flags = be32toh(header_64->p_flags);
|
||||||
|
header->p_offset = be64toh(header_64->p_offset);
|
||||||
|
header->p_vaddr = be64toh(header_64->p_vaddr);
|
||||||
|
header->p_paddr = be64toh(header_64->p_paddr);
|
||||||
|
header->p_filesz = be64toh(header_64->p_filesz);
|
||||||
|
header->p_memsz = be64toh(header_64->p_memsz);
|
||||||
|
header->p_align = be64toh(header_64->p_align);
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an ELF section header. We use the 64-bit structure
|
||||||
|
* `struct elf_section_header` as the output structure.
|
||||||
|
*
|
||||||
|
* On error, the negative error code is returned.
|
||||||
|
* On success, 0 is returned, and the header is written in the given parameter.
|
||||||
|
*/
|
||||||
|
static int parse_elf_section_header(const char *code,
|
||||||
|
const struct elf_header *elf,
|
||||||
|
struct elf_section_header *header)
|
||||||
|
{
|
||||||
|
struct elf_section_header *header_64;
|
||||||
|
struct elf_section_header_32 *header_32;
|
||||||
|
|
||||||
|
if (ELF_ENDIAN_LE(elf->e_indent)) {
|
||||||
|
if (ELF_BITS_32(elf->e_indent)) {
|
||||||
|
header_32 = (struct elf_section_header_32 *)code;
|
||||||
|
header->sh_name = le32toh(header_32->sh_name);
|
||||||
|
header->sh_type = le32toh(header_32->sh_type);
|
||||||
|
header->sh_flags = le32toh(header_32->sh_flags);
|
||||||
|
header->sh_addr = le32toh(header_32->sh_addr);
|
||||||
|
header->sh_offset = le32toh(header_32->sh_offset);
|
||||||
|
header->sh_size = le32toh(header_32->sh_size);
|
||||||
|
header->sh_link = le32toh(header_32->sh_link);
|
||||||
|
header->sh_info = le32toh(header_32->sh_info);
|
||||||
|
header->sh_addralign = le32toh(header_32->sh_addralign);
|
||||||
|
header->sh_entsize = le32toh(header_32->sh_entsize);
|
||||||
|
} else if (ELF_BITS_64(elf->e_indent)) {
|
||||||
|
header_64 = (struct elf_section_header *)code;
|
||||||
|
header->sh_name = le32toh(header_64->sh_name);
|
||||||
|
header->sh_type = le32toh(header_64->sh_type);
|
||||||
|
header->sh_flags = le64toh(header_64->sh_flags);
|
||||||
|
header->sh_addr = le64toh(header_64->sh_addr);
|
||||||
|
header->sh_offset = le64toh(header_64->sh_offset);
|
||||||
|
header->sh_size = le64toh(header_64->sh_size);
|
||||||
|
header->sh_link = le32toh(header_64->sh_link);
|
||||||
|
header->sh_info = le32toh(header_64->sh_info);
|
||||||
|
header->sh_addralign = le64toh(header_64->sh_addralign);
|
||||||
|
header->sh_entsize = le64toh(header_64->sh_entsize);
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else if (ELF_ENDIAN_BE(elf->e_indent)) {
|
||||||
|
if (ELF_BITS_32(elf->e_indent)) {
|
||||||
|
header_32 = (struct elf_section_header_32 *)code;
|
||||||
|
header->sh_name = be32toh(header_32->sh_name);
|
||||||
|
header->sh_type = be32toh(header_32->sh_type);
|
||||||
|
header->sh_flags = be32toh(header_32->sh_flags);
|
||||||
|
header->sh_addr = be32toh(header_32->sh_addr);
|
||||||
|
header->sh_offset = be32toh(header_32->sh_offset);
|
||||||
|
header->sh_size = be32toh(header_32->sh_size);
|
||||||
|
header->sh_link = be32toh(header_32->sh_link);
|
||||||
|
header->sh_info = be32toh(header_32->sh_info);
|
||||||
|
header->sh_addralign = be32toh(header_32->sh_addralign);
|
||||||
|
header->sh_entsize = be32toh(header_32->sh_entsize);
|
||||||
|
} else if (ELF_BITS_64(elf->e_indent)) {
|
||||||
|
header_64 = (struct elf_section_header *)code;
|
||||||
|
header->sh_name = be32toh(header_64->sh_name);
|
||||||
|
header->sh_type = be32toh(header_64->sh_type);
|
||||||
|
header->sh_flags = be64toh(header_64->sh_flags);
|
||||||
|
header->sh_addr = be64toh(header_64->sh_addr);
|
||||||
|
header->sh_offset = be64toh(header_64->sh_offset);
|
||||||
|
header->sh_size = be64toh(header_64->sh_size);
|
||||||
|
header->sh_link = be32toh(header_64->sh_link);
|
||||||
|
header->sh_info = be32toh(header_64->sh_info);
|
||||||
|
header->sh_addralign = be64toh(header_64->sh_addralign);
|
||||||
|
header->sh_entsize = be64toh(header_64->sh_entsize);
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void elf_free(struct elf_file *elf)
|
||||||
|
{
|
||||||
|
if (elf->s_headers)
|
||||||
|
free(elf->s_headers);
|
||||||
|
if (elf->p_headers)
|
||||||
|
free(elf->p_headers);
|
||||||
|
free(elf);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct elf_file *elf_parse_file(const char *code)
|
||||||
|
{
|
||||||
|
struct elf_file *elf;
|
||||||
|
int err;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
elf = malloc(sizeof(*elf));
|
||||||
|
if (!elf)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
err = parse_elf_header(code, &elf->header);
|
||||||
|
if (err)
|
||||||
|
goto out_free_elf;
|
||||||
|
|
||||||
|
/* Allocate memory for program headers and section headers */
|
||||||
|
err = -ENOMEM;
|
||||||
|
elf->p_headers =
|
||||||
|
malloc(elf->header.e_phentsize * elf->header.e_phnum);
|
||||||
|
if (!elf->p_headers)
|
||||||
|
goto out_free_elf;
|
||||||
|
elf->s_headers =
|
||||||
|
malloc(elf->header.e_shentsize * elf->header.e_shnum);
|
||||||
|
if (!elf->s_headers)
|
||||||
|
goto out_free_elf_p;
|
||||||
|
|
||||||
|
/* Parse program headers and section headers */
|
||||||
|
for (i = 0; i < elf->header.e_phnum; ++i) {
|
||||||
|
err = parse_elf_program_header(code + elf->header.e_phoff +
|
||||||
|
elf->header.e_phentsize * i,
|
||||||
|
&elf->header,
|
||||||
|
&elf->p_headers[i]);
|
||||||
|
if (err)
|
||||||
|
goto out_free_all;
|
||||||
|
}
|
||||||
|
for (i = 0; i < elf->header.e_shnum; ++i) {
|
||||||
|
err = parse_elf_section_header(code + elf->header.e_shoff +
|
||||||
|
elf->header.e_shentsize * i,
|
||||||
|
&elf->header,
|
||||||
|
&elf->s_headers[i]);
|
||||||
|
if (err)
|
||||||
|
goto out_free_all;
|
||||||
|
}
|
||||||
|
return elf;
|
||||||
|
|
||||||
|
out_free_all:
|
||||||
|
free(elf->s_headers);
|
||||||
|
out_free_elf_p:
|
||||||
|
free(elf->p_headers);
|
||||||
|
out_free_elf:
|
||||||
|
free(elf);
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ELF format according to
|
||||||
|
* https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define EI_MAG_SIZE 4
|
||||||
|
|
||||||
|
#define PT_NULL 0x00000000
|
||||||
|
#define PT_LOAD 0x00000001
|
||||||
|
#define PT_DYNAMIC 0x00000002
|
||||||
|
#define PT_INTERP 0x00000003
|
||||||
|
#define PT_NOTE 0x00000004
|
||||||
|
#define PT_SHLIB 0x00000005
|
||||||
|
#define PT_PHDR 0x00000006
|
||||||
|
#define PT_LOOS 0x60000000
|
||||||
|
#define PT_HIOS 0x6fffffff
|
||||||
|
#define PT_LOPROC 0x70000000
|
||||||
|
#define PT_HIRPOC 0x7fffffff
|
||||||
|
|
||||||
|
#define PF_ALL 0x7
|
||||||
|
#define PF_X 0x1
|
||||||
|
#define PF_W 0x2
|
||||||
|
#define PF_R 0x4
|
||||||
|
|
||||||
|
typedef uint64_t u64;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef uint8_t u8;
|
||||||
|
|
||||||
|
typedef int64_t s64;
|
||||||
|
typedef int32_t s32;
|
||||||
|
typedef int16_t s16;
|
||||||
|
typedef int8_t s8;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This part of ELF header is endianness-independent.
|
||||||
|
*/
|
||||||
|
struct elf_indent {
|
||||||
|
u8 ei_magic[4];
|
||||||
|
u8 ei_class;
|
||||||
|
u8 ei_data;
|
||||||
|
u8 ei_version;
|
||||||
|
u8 ei_osabi;
|
||||||
|
u8 ei_abiversion;
|
||||||
|
u8 ei_pad[7];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ELF header format. One should check the `e_indent` to decide the endianness.
|
||||||
|
*/
|
||||||
|
struct elf_header {
|
||||||
|
struct elf_indent e_indent;
|
||||||
|
u16 e_type;
|
||||||
|
u16 e_machine;
|
||||||
|
u32 e_version;
|
||||||
|
u64 e_entry;
|
||||||
|
u64 e_phoff;
|
||||||
|
u64 e_shoff;
|
||||||
|
u32 e_flags;
|
||||||
|
u16 e_ehsize;
|
||||||
|
u16 e_phentsize;
|
||||||
|
u16 e_phnum;
|
||||||
|
u16 e_shentsize;
|
||||||
|
u16 e_shnum;
|
||||||
|
u16 e_shstrndx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 32-Bit of the elf_header. Check the `e_indent` first to decide.
|
||||||
|
*/
|
||||||
|
struct elf_header_32 {
|
||||||
|
struct elf_indent e_indent;
|
||||||
|
u16 e_type;
|
||||||
|
u16 e_machine;
|
||||||
|
u32 e_version;
|
||||||
|
u32 e_entry;
|
||||||
|
u32 e_phoff;
|
||||||
|
u32 e_shoff;
|
||||||
|
u32 e_flags;
|
||||||
|
u16 e_ehsize;
|
||||||
|
u16 e_phentsize;
|
||||||
|
u16 e_phnum;
|
||||||
|
u16 e_shentsize;
|
||||||
|
u16 e_shnum;
|
||||||
|
u16 e_shstrndx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct elf_program_header {
|
||||||
|
u32 p_type;
|
||||||
|
u32 p_flags;
|
||||||
|
u64 p_offset;
|
||||||
|
u64 p_vaddr;
|
||||||
|
u64 p_paddr;
|
||||||
|
u64 p_filesz;
|
||||||
|
u64 p_memsz;
|
||||||
|
u64 p_align;
|
||||||
|
};
|
||||||
|
struct elf_program_header_32 {
|
||||||
|
u32 p_type;
|
||||||
|
u32 p_offset;
|
||||||
|
u32 p_vaddr;
|
||||||
|
u32 p_paddr;
|
||||||
|
u32 p_filesz;
|
||||||
|
u32 p_memsz;
|
||||||
|
u32 p_flags;
|
||||||
|
u32 p_align;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct elf_section_header {
|
||||||
|
u32 sh_name;
|
||||||
|
u32 sh_type;
|
||||||
|
u64 sh_flags;
|
||||||
|
u64 sh_addr;
|
||||||
|
u64 sh_offset;
|
||||||
|
u64 sh_size;
|
||||||
|
u32 sh_link;
|
||||||
|
u32 sh_info;
|
||||||
|
u64 sh_addralign;
|
||||||
|
u64 sh_entsize;
|
||||||
|
};
|
||||||
|
struct elf_section_header_32 {
|
||||||
|
u32 sh_name;
|
||||||
|
u32 sh_type;
|
||||||
|
u32 sh_flags;
|
||||||
|
u32 sh_addr;
|
||||||
|
u32 sh_offset;
|
||||||
|
u32 sh_size;
|
||||||
|
u32 sh_link;
|
||||||
|
u32 sh_info;
|
||||||
|
u32 sh_addralign;
|
||||||
|
u32 sh_entsize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct elf_file {
|
||||||
|
struct elf_header header;
|
||||||
|
struct elf_program_header *p_headers;
|
||||||
|
struct elf_section_header *s_headers;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct elf_file *elf_parse_file(const char *code);
|
||||||
|
void elf_free(struct elf_file *elf);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,99 @@
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include "elf.h"
|
||||||
|
|
||||||
|
#define le16_to_cpu(x) (x)
|
||||||
|
#define le32_to_cpu(x) (x)
|
||||||
|
#define le64_to_cpu(x) (x)
|
||||||
|
|
||||||
|
#define be16_to_cpu(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))
|
||||||
|
#define be32_to_cpu(x) ((be16_to_cpu((x)) << 16) | (be16_to_cpu((x) >> 16)))
|
||||||
|
#define be64_to_cpu(x) ((be32_to_cpu((x)) << 32) | (be32_to_cpu((x) >> 32)))
|
||||||
|
|
||||||
|
|
||||||
|
#define be128ptr_to_cpu_hi(x) (be64_to_cpu(*(u64 *)(x)))
|
||||||
|
#define be128ptr_to_cpu_lo(x) (be64_to_cpu(*((u64 *)(x) + 1)))
|
||||||
|
|
||||||
|
#define be96ptr_to_cpu_hi(x) (be32_to_cpu(*(u32 *)(x)))
|
||||||
|
#define be96ptr_to_cpu_lo(x) (((u64)(be32_to_cpu(*((u32 *)(x) + 1)))) << 32 | \
|
||||||
|
(be32_to_cpu(*((u32 *)(x)) + 2)))
|
||||||
|
|
||||||
|
struct elf_info {
|
||||||
|
u64 mem_size;
|
||||||
|
u64 entry;
|
||||||
|
u64 flags;
|
||||||
|
u64 phentsize;
|
||||||
|
u64 phnum;
|
||||||
|
u64 phdr_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
void get_elf_info(const char *binary, struct elf_info *info)
|
||||||
|
{
|
||||||
|
struct elf_file *elf;
|
||||||
|
int i;
|
||||||
|
u64 size = 0;
|
||||||
|
|
||||||
|
elf = elf_parse_file(binary);
|
||||||
|
elf->header.e_phentsize;
|
||||||
|
if (!elf) {
|
||||||
|
printf("parse elf fail\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = 0; i < elf->header.e_phnum; ++i) {
|
||||||
|
if (elf->p_headers[i].p_type != PT_LOAD)
|
||||||
|
continue;
|
||||||
|
size += elf->p_headers[i].p_memsz;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->entry = elf->header.e_entry;
|
||||||
|
info->flags = elf->header.e_flags;
|
||||||
|
info->mem_size = size;
|
||||||
|
info->phentsize = elf->header.e_phentsize;
|
||||||
|
info->phnum = elf->header.e_phnum;
|
||||||
|
info->phdr_addr = elf->p_headers[0].p_vaddr + elf->header.e_phoff;
|
||||||
|
free(elf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
struct stat st;
|
||||||
|
char *buf;
|
||||||
|
struct elf_info info;
|
||||||
|
|
||||||
|
if (argc == 1) {
|
||||||
|
printf("Need a path points to the procmgr.elf\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(argv[1], O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
printf("Can not open elf file!\n");
|
||||||
|
}
|
||||||
|
fstat(fd, &st);
|
||||||
|
buf = malloc(st.st_size);
|
||||||
|
read(fd, buf, st.st_size);
|
||||||
|
get_elf_info(buf, &info);
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
close(fd);
|
||||||
|
info.entry = be64_to_cpu(info.entry);
|
||||||
|
info.flags = be64_to_cpu(info.flags);
|
||||||
|
info.mem_size = be64_to_cpu(info.mem_size);
|
||||||
|
info.phentsize = be64_to_cpu(info.phentsize);
|
||||||
|
info.phnum = be64_to_cpu(info.phnum);
|
||||||
|
info.phdr_addr = be64_to_cpu(info.phdr_addr);
|
||||||
|
|
||||||
|
fd = open("./elf_info.temp", O_CREAT | O_RDWR);
|
||||||
|
if (fd < 0) {
|
||||||
|
printf("Create file failed!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
write(fd, (void *)&info, sizeof(struct elf_info));
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -49,18 +49,6 @@ add_custom_command(
|
||||||
DEPENDS)
|
DEPENDS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# libevent is already installed in docker images
|
|
||||||
if(CHCORE_DEMOS_LIBEVENT)
|
|
||||||
add_custom_target(
|
|
||||||
libevent ALL
|
|
||||||
WORKING_DIRECTORY ${build_demos_dir}/libevent
|
|
||||||
COMMAND ./autogen.sh
|
|
||||||
COMMAND ./configure --disable-openssl --disable-debug-mode --disable-samples -prefix=/usr/libevent
|
|
||||||
COMMAND CC=${CHCORE_MUSL_LIBC_INSTALL_DIR}/bin/musl-gcc -s &> /dev/null
|
|
||||||
COMMAND make install
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CHCORE_DEMOS_MEMCACHED)
|
if(CHCORE_DEMOS_MEMCACHED)
|
||||||
add_custom_target(
|
add_custom_target(
|
||||||
memcached ALL
|
memcached ALL
|
||||||
|
|
|
@ -21,6 +21,7 @@ SHORT_TERM_TODO
|
||||||
release.h
|
release.h
|
||||||
src/transfer.sh
|
src/transfer.sh
|
||||||
src/configs
|
src/configs
|
||||||
|
src/redis-test
|
||||||
redis.ds
|
redis.ds
|
||||||
src/redis.conf
|
src/redis.conf
|
||||||
src/nodes.conf
|
src/nodes.conf
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
chcore_enable_clang_tidy()
|
# chcore_enable_clang_tidy()
|
||||||
add_executable(chcore_shell.bin chcore_shell.c job_control.c buildin_cmd.c
|
add_executable(chcore_shell.bin chcore_shell.c job_control.c buildin_cmd.c
|
||||||
handle_input.c)
|
handle_input.c)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
chcore_enable_clang_tidy(
|
# chcore_enable_clang_tidy(
|
||||||
EXTRA_CHECKS -bugprone-implicit-widening-of-multiplication-result)
|
# EXTRA_CHECKS -bugprone-implicit-widening-of-multiplication-result)
|
||||||
add_library(fat32_3rd_party OBJECT ffunicode.c ff.c)
|
add_library(fat32_3rd_party OBJECT ffunicode.c ff.c)
|
||||||
chcore_enable_clang_tidy()
|
# chcore_enable_clang_tidy()
|
||||||
add_executable(fat32.srv diskio.c ff_server.c ffsystem.c fat32_ops.c
|
add_executable(fat32.srv diskio.c ff_server.c ffsystem.c fat32_ops.c
|
||||||
fat32_utils.c)
|
fat32_utils.c)
|
||||||
target_link_libraries(fat32.srv PRIVATE fs_base fat32_3rd_party)
|
target_link_libraries(fat32.srv PRIVATE fs_base fat32_3rd_party)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
chcore_enable_clang_tidy()
|
# chcore_enable_clang_tidy()
|
||||||
add_executable(procmgr.srv proc_node.c procmgr.c recycle.c shell_msg_handler.c
|
add_executable(procmgr.srv proc_node.c procmgr.c recycle.c shell_msg_handler.c
|
||||||
srvmgr.c)
|
srvmgr.c)
|
||||||
target_link_options(procmgr.srv PRIVATE -static)
|
target_link_options(procmgr.srv PRIVATE -static)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
chcore_enable_clang_tidy(
|
# chcore_enable_clang_tidy(
|
||||||
EXTRA_CHECKS -performance-no-int-to-ptr,-clang-analyzer-deadcode.DeadStores)
|
# EXTRA_CHECKS -performance-no-int-to-ptr,-clang-analyzer-deadcode.DeadStores)
|
||||||
message("CMAKE_C_CLANG_TIDY: ${CMAKE_C_CLANG_TIDY}")
|
message("CMAKE_C_CLANG_TIDY: ${CMAKE_C_CLANG_TIDY}")
|
||||||
set(binary_path ${build_user_dir}/ramdisk.cpio)
|
set(binary_path ${build_user_dir}/ramdisk.cpio)
|
||||||
configure_file(incbin_ramdisk.tpl.S incbin_ramdisk.S)
|
configure_file(incbin_ramdisk.tpl.S incbin_ramdisk.S)
|
||||||
|
|
Loading…
Reference in New Issue