Good in study, attitude and health

操作系统-PA1

时长:15h

STFSC


nemu/include/macro.h


  • 宏&预处理运算符

    • https://en.cppreference.com/w/cpp/preprocessor
    • https://en.cppreference.com/w/cpp/preprocessor/replace#Predefined_macros(来自上面的链接)
// macro stringizing
#define str_temp(x) #x
#define str(x) str_temp(x)

使用两个宏逐步展开,是为了防止 x 传入一个宏,如

#define VALUE 42
#define str_temp(x) #x      // 第一层,字符串化操作
#define str(x) str_temp(x)  // 第二层,触发宏参数的展开

str(VALUE)

展开过程:

  • str(VALUE) 被替换为 str_temp(VALUE)(第一层宏只将 VALUE 传递给第二层)。
  • 在展开 str_temp(VALUE) 时:VALUE 首先被展开为 42,然后作为 std_temp 的参数传入。然后 str_temp 对 42 应用字符串化操作符 #,结果是 “42”。通过这种设计,VALUE 的定义得以在字符串化之前完成展开。

如果是单层宏:

#define VALUE 42
#define str(x) #x

调用 str(VALUE)

  1. str(VALUE) 被替换为 #VALUE
  2. VALUE 不会展开,因为 # 直接作用于参数 x,预处理器认为它是字符串化操作符的目标。
  3. 最终结果是 "VALUE"

之后的##的拼接操作亦是如此。

宏分析


// macro.h
// 总是返回第二个参数
#define CHOOSE2nd(a, b, ...) b

#define MUX_WITH_COMMA(contain_comma, a, b) CHOOSE2nd(contain_comma a, b)
#define MUX_MACRO_PROPERTY(p, macro, a, b) MUX_WITH_COMMA(concat(p, macro), a, b)
// define placeholders for some property
// 占位符
#define __P_DEF_0 X,
#define __P_DEF_1 X,
// define some selection functions based on the properties of BOOLEAN macro
#define MUXDEF(macro, X, Y) MUX_MACRO_PROPERTY(__P_DEF_, macro, X, Y)

//utils.h
// 给str格式化输出,最终还要恢复默认格式,使后续文本不受影响
#define ANSI_FMT(str, fmt) fmt str ANSI_NONE
  Log("Trace: %s", MUXDEF(CONFIG_TRACE, ANSI_FMT("ON", ANSI_FG_GREEN), ANSI_FMT("OFF", ANSI_FG_RED)));
  • MUXDEF
    • macro: CONFIG_TRACE
    • X: ANSI_FMT(“ON”, ANSI_FG_GREEN)
    • Y: ANSI_FMT(“OFF, ANSI_FG_RED)
  • MUX_MACRO_PROPERTY
    • p: _P_DEF
    • macro: 0/1
    • a: ON(GREEN)
    • b: OFF(RED)
  • MUX_WITH_COMMA
    • contain_comma: P_DEF_0/P_DEF_1
    • a: ON(GREEN)
    • b: OFF(RED)
  • CHOOSE2nd
    • a: X, ON(GREEN)
    • b: OFF(RED) …

只要 CONFIG_TRACE 有值(被定义,无论是 0 还是 1)就输出 ON,否则输出 OFF。ChatGPT 骗了我半天,说 1 会输出 ON,0 会输出 OFF。

资料


  • Mario ROM 下载地址: https://www.romsgames.net/nintendo-rom-mario-bros/
  • 指令集架构: https://en.wikipedia.org/wiki/Instruction_set_architecture
  • RISC-V: https://github.com/riscv-non-isa/riscv-elf-psabi-doc

build.mk

# 设置默认目标,如果make指令未指定目标,会构建app目标
.DEFAULT_GOAL = app

# Add necessary options if the target is a shared library
# 如果share是1,则假设目标是一个共享库
ifeq ($(SHARE),1)
# 给生成的文件附加后缀,用于区分共享库
SO = -so
# 生成与位置无关的代码,共享库必须 隐藏非导出的符号
CFLAGS  += -fPIC -fvisibility=hidden
# 告诉链接器生成共享库 同样应用于链接时
LDFLAGS += -shared -fPIC
endif
# 通过pwd获取当前工作目录的路径
work_dir  = $(shell pwd)
# 生成目录,用于存储中间文件和目标文件
BUILD_DIR = $(WORK_DIR)/build
# 包含目录的路径,默认为 include 文件夹,还可以通过外部传递额外路径。

INC_PATH := $(WORK_DIR)/include $(INC_PATH)
# 存放编译生成的目标文件(.o 文件)的目录,路径会根据目标名称和共享库后缀变化。
OBJ_DIR  = $(BUILD_DIR)/obj-$(NAME)$(SO)
# 最终生成的目标文件路径。
BINARY   = $(BUILD_DIR)/$(NAME)$(SO)
# Compilation flags
# 如果 CC=clang,使用 clang++ 作为 C++ 编译器;否则使用 g++。
ifeq ($(CC),clang)
CXX := clang++
else
CXX := g++
endif
# 链接器也是用CXX
LD := $(CXX)
INCLUDES = $(addprefix -I, $(INC_PATH))
# -O2:启用编译器优化。
# -MMD:生成依赖文件(.d),用于追踪头文件变化。
# -Wall:启用所有警告。
# -Werror:将警告视为错误。
# $(INCLUDES):包含路径,添加为 -I 选项。
CFLAGS  := -O2 -MMD -Wall -Werror $(INCLUDES) $(CFLAGS)
# -O2:优化链接。
LDFLAGS := -O2 $(LDFLAGS)

# OBJS:所有源文件的目标文件(.o)。
# 将 SRCS 中的 .c 文件替换为对应的 .o。
# 将 CXXSRC 中的 .cc 文件替换为对应的 .o。
OBJS = $(SRCS:%.c=$(OBJ_DIR)/%.o) $(CXXSRC:%.cc=$(OBJ_DIR)/%.o)

# Makefile的编译规则在此处定义
# Compilation patterns
# $<:当前依赖的文件(如源文件).c
# $@:目标文件.o
# 打印编译信息(+ CC file.c)。
# 创建目标文件目录。
# 编译 C 文件生成目标文件。
# 调用 call_fixdep 修复依赖文件(可能定义在别处)。
$(OBJ_DIR)/%.o: %.c
	@echo + CC $<
	@mkdir -p $(dir $@)
	@$(CC) $(CFLAGS) -c -o $@ $<
	$(call call_fixdep, $(@:.o=.d), $@)
# C++文件 后缀和编译器不同
$(OBJ_DIR)/%.o: %.cc
	@echo + CXX $<
	@mkdir -p $(dir $@)
	@$(CXX) $(CFLAGS) $(CXXFLAGS) -c -o $@ $<
	$(call call_fixdep, $(@:.o=.d), $@)

# Depencies
# 自动包含所有 .d 依赖文件,确保在头文件变化时重新编译对应的目标文件。
-include $(OBJS:.o=.d)

# Some convenient rules
#  Makefile 中,目标通常与文件关联,比如 app 或 clean 可能会和同名的文件(app 或 clean)冲突。如果一个实际文件与目标同名,Make 默认会认为目标已经“生成”,从而跳过规则的执行。
# .PHONY 的作用是告诉 Make:
# 即使存在与目标同名的文件,也不要将其视为文件目标,而是始终执行目标规则。
.PHONY: app clean

app: $(BINARY)

$(BINARY):: $(OBJS) $(ARCHIVES)
	@echo + LD $@
	@$(LD) -o $@ $(OBJS) $(LDFLAGS) $(ARCHIVES) $(LIBS)

# 删除构建目录,清理所有生成文件。
clean:
	-rm -rf $(BUILD_DIR)

#*******************************

Copyright (c) 2014-2024 Zihao Yu, Nanjing University

#

NEMU is licensed under Mulan PSL v2.

You can use this software according to the terms and conditions of the Mulan PSL v2.

You may obtain a copy of Mulan PSL v2 at:

http://license.coscl.org.cn/MulanPSL2

#

THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT WARRANTIES OF ANY KIND,

EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,

MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.

#

See the Mulan PSL v2 for more details.

#******************************/

Sanity check

ifeq ($(wildcard $(NEMU_HOME)/src/nemu-main.c),) $(error NEMU_HOME=$(NEMU_HOME) is not a NEMU repo) endif

Include variables and rules generated by menuconfig

-include $(NEMU_HOME)/include/config/auto.conf -include $(NEMU_HOME)/include/config/auto.conf.cmd

remove_quote = $(patsubst “%”,%,$(1))

Extract variabls from menuconfig

GUEST_ISA ?= $(call remove_quote,$(CONFIG_ISA)) ENGINE ?= $(call remove_quote,$(CONFIG_ENGINE)) NAME = $(GUEST_ISA)-nemu-$(ENGINE)

Include all filelist.mk to merge file lists

FILELIST_MK = $(shell find -L ./src -name “filelist.mk”) include $(FILELIST_MK)

Filter out directories and files in blacklist to obtain the final set of source files

DIRS-BLACKLIST-y += $(DIRS-BLACKLIST) SRCS-BLACKLIST-y += $(SRCS-BLACKLIST) $(shell find -L $(DIRS-BLACKLIST-y) -name “.c”) SRCS-y += $(shell find -L $(DIRS-y) -name “.c”) SRCS = $(filter-out $(SRCS-BLACKLIST-y),$(SRCS-y))

Extract compiler and options from menuconfig

ifneq ($(CONFIG_CC),) CC = $(call remove_quote,$(CONFIG_CC)) endif CFLAGS_BUILD += $(call remove_quote,$(CONFIG_CC_OPT)) CFLAGS_BUILD += $(if $(CONFIG_CC_LTO),-flto,) CFLAGS_BUILD += $(if $(CONFIG_CC_DEBUG),-Og -ggdb3,) CFLAGS_BUILD += $(if $(CONFIG_CC_ASAN),-fsanitize=address,) CFLAGS_TRACE += -DITRACE_COND=$(if $(CONFIG_ITRACE_COND),$(call remove_quote,$(CONFIG_ITRACE_COND)),true) CFLAGS += $(CFLAGS_BUILD) $(CFLAGS_TRACE) -D__GUEST_ISA__=$(GUEST_ISA) LDFLAGS += $(CFLAGS_BUILD)

Include rules for menuconfig

include $(NEMU_HOME)/scripts/config.mk

ifdef CONFIG_TARGET_AM include $(AM_HOME)/Makefile LINKAGE += $(ARCHIVES) else

Include rules to build NEMU

include $(NEMU_HOME)/scripts/native.mk endif