如何在Makefile中实现单规则多目标?内核驱动编译报错
问题
我正在编译Windows内核驱动,现有Makefile通过all目标依次完成编译、链接、签名操作。希望改用单规则多目标的方式实现,而非仅依赖all目标。
现有Makefile内容如下:
PLATFORM := x64 CONFIGURATION := debug PROJECT_NAME := Necrobyte BIN_PATH := "bin\$(PLATFORM)\$(CONFIGURATION)\" OUT_PATH := "build\$(PLATFORM)\$(CONFIGURATION)\" # ++ COMPILE ++ SRC_FILES := $(wildcard *.c) CC := "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\bin\HostX86\x64\CL.exe" CFLAGS := /c /Ibin\$(PLATFORM)\$(CONFIGURATION)\ /Zi /nologo /W4 /WX /diagnostics:column /Od /Oi /Oy- /D _WIN64 /D _AMD64_ /D AMD64 \ /D DEPRECATE_DDK_FUNCTIONS=1 /D MSC_NOOPT /D _WIN32_WINNT=0x0A00 /D WINVER=0x0A00 /D WINNT=1 \ /D NTDDI_VERSION=0xA00000C /D DBG=1 /D KMDF_VERSION_MAJOR=1 /D KMDF_VERSION_MINOR=15 /GF /Gm- /Zp8 \ /GS /guard:cf /Gy /fp:precise /Qspectre /Zc:wchar_t- /Zc:forScope /Zc:inline /GR- /Fo"bin\$(PLATFORM)\$(CONFIGURATION)\\" \ /external:W4 /Gz /wd4748 /wd4603 /wd4627 /wd4986 /wd4987 \ /FC /errorReport:queue /kernel -cbstring -d2epilogunwind /d1import_no_registry /d2AllowCompatibleILVersions \ /d2Zi+ CINCLUDE := /FI"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\shared\warning.h" \ /I"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include" \ /I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\km" \ /I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt" \ /I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\shared" # -- LINK -- SRC_OBJ := $(patsubst %.c, $(BIN_PATH)%.obj, $(SRC_FILES)) LNK := "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\bin\HostX86\x64\link.exe" LFLAGS := /ERRORREPORT:QUEUE /OUT:"C:\Users\ihxd\Desktop\Necrobyte\build\x64\debug\$(PROJECT_NAME).sys" \ /VERSION:"10.0" /INCREMENTAL:NO /NOLOGO /WX /SECTION:"INIT,d" \ /NODEFAULTLIB /MANIFEST:NO /DEBUG \ /PDB:"$(BIN_PATH)$(PROJECT_NAME).pdb" \ /SUBSYSTEM:NATIVE,"10.00" /Driver /OPT:REF /OPT:ICF /ENTRY:"FxDriverEntry" /RELEASE \ /IMPLIB:"$(BIN_PATH)$(PROJECT_NAME).lib" \ /MERGE:"_TEXT=.text;_PAGE=PAGE" /MACHINE:X64 /PROFILE /guard:cf /kernel \ /IGNORE:4198,4010,4037,4039,4065,4070,4078,4087,4089,4221,4108,4088,4218,4235 \ /osversion:10.0 /pdbcompress /debugtype:pdata \ LINCLUDE := "C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\km\x64\netio.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\km\x64\BufferOverflowFastFailK.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\km\x64\ntoskrnl.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\km\x64\hal.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\km\x64\wmilib.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\wdf\kmdf\x64\1.15\WdfLdr.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\wdf\kmdf\x64\1.15\WdfDriverEntry.lib" # ** TEST SIGN ** SGN := "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe" sign /ph /fd "sha256" /sha1 "9606C369B02FF06D537D40268DCCE5DB4D1E5FF7" all: $(CC) $(CINCLUDE) $(CFLAGS) $(SRC_FILES) $(LNK) $(LFLAGS) $(LINCLUDE) $(SRC_OBJ) $(SGN) $(OUT_PATH)$(PROJECT_NAME).sys
尝试修改为如下规则后,出现错误提示Multiple targets. STOP:
$(OUT_PATH): $(SRC_FILES) $(CC) $(CINCLUDE) $(CFLAGS) $(SRC_FILES)
错误原因
直接用$(OUT_PATH)(目录路径)作为目标,同时依赖多个源文件时,Make会将其误解为"多目标映射"——即试图把多个源文件对应到同一个目录目标,触发匹配冲突,从而抛出Multiple targets错误。
解决方案
要实现单规则多目标的链式流程,需明确**最终产物(签名后的sys文件)**为核心目标,让编译、链接、签名步骤自动依赖前置产物,替代all目标。以下是修改后的完整Makefile:
PLATFORM := x64 CONFIGURATION := debug PROJECT_NAME := Necrobyte # 简化路径定义,去掉不必要的引号 BIN_PATH := bin\$(PLATFORM)\$(CONFIGURATION) OUT_PATH := build\$(PLATFORM)\$(CONFIGURATION) FINAL_SYS := $(OUT_PATH)\$(PROJECT_NAME).sys SIGNED_SYS := $(FINAL_SYS).signed # 区分签名前后的文件,也可直接覆盖原sys # ++ COMPILE ++ SRC_FILES := $(wildcard *.c) SRC_OBJ := $(patsubst %.c, $(BIN_PATH)/%.obj, $(SRC_FILES)) CC := "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\bin\HostX86\x64\CL.exe" CFLAGS := /c /I$(BIN_PATH)/ /Zi /nologo /W4 /WX /diagnostics:column /Od /Oi /Oy- /D _WIN64 /D _AMD64_ /D AMD64 \ /D DEPRECATE_DDK_FUNCTIONS=1 /D MSC_NOOPT /D _WIN32_WINNT=0x0A00 /D WINVER=0x0A00 /D WINNT=1 \ /D NTDDI_VERSION=0xA00000C /D DBG=1 /D KMDF_VERSION_MAJOR=1 /D KMDF_VERSION_MINOR=15 /GF /Gm- /Zp8 \ /GS /guard:cf /Gy /fp:precise /Qspectre /Zc:wchar_t- /Zc:forScope /Zc:inline /GR- /Fo$(BIN_PATH)/ \ /external:W4 /Gz /wd4748 /wd4603 /wd4627 /wd4986 /wd4987 \ /FC /errorReport:queue /kernel -cbstring -d2epilogunwind /d1import_no_registry /d2AllowCompatibleILVersions \ /d2Zi+ CINCLUDE := /FI"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\shared\warning.h" \ /I"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\include" \ /I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\km" \ /I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt" \ /I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\shared" # -- LINK -- LNK := "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.39.33519\bin\HostX86\x64\link.exe" LFLAGS := /ERRORREPORT:QUEUE /OUT:$(FINAL_SYS) \ /VERSION:"10.0" /INCREMENTAL:NO /NOLOGO /WX /SECTION:"INIT,d" \ /NODEFAULTLIB /MANIFEST:NO /DEBUG \ /PDB:$(BIN_PATH)/$(PROJECT_NAME).pdb \ /SUBSYSTEM:NATIVE,"10.00" /Driver /OPT:REF /OPT:ICF /ENTRY:"FxDriverEntry" /RELEASE \ /IMPLIB:$(BIN_PATH)/$(PROJECT_NAME).lib \ /MERGE:"_TEXT=.text;_PAGE=PAGE" /MACHINE:X64 /PROFILE /guard:cf /kernel \ /IGNORE:4198,4010,4037,4039,4065,4070,4078,4087,4089,4221,4108,4088,4218,4235 \ /osversion:10.0 /pdbcompress /debugtype:pdata \ LINCLUDE := "C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\km\x64\netio.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\km\x64\BufferOverflowFastFailK.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\km\x64\ntoskrnl.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\km\x64\hal.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\km\x64\wmilib.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\wdf\kmdf\x64\1.15\WdfLdr.lib" \ "C:\Program Files (x86)\Windows Kits\10\lib\wdf\kmdf\x64\1.15\WdfDriverEntry.lib" # ** TEST SIGN ** SGN := "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe" sign /ph /fd "sha256" /sha1 "9606C369B02FF06D537D40268DCCE5DB4D1E5FF7" # 创建必要目录(顺序依赖,确保编译前目录存在) $(BIN_PATH) $(OUT_PATH): @if not exist "$@" mkdir "$@" # 单源文件编译规则:每个.c对应生成.obj $(BIN_PATH)/%.obj: %.c | $(BIN_PATH) $(CC) $(CINCLUDE) $(CFLAGS) $< # 链接规则:依赖所有.obj生成sys文件 $(FINAL_SYS): $(SRC_OBJ) | $(OUT_PATH) $(LNK) $(LFLAGS) $(LINCLUDE) $^ # 签名规则:依赖未签名sys生成签名后产物(单规则多目标核心) $(SIGNED_SYS): $(FINAL_SYS) $(SGN) $< copy $< $@ # 保留原文件,生成签名副本;若要直接覆盖,替换为$(SGN) $@ # 设置默认目标为签名后的文件,直接执行make即可完成全流程 .DEFAULT_GOAL := $(SIGNED_SYS)
关键修改说明
- 目标层级拆分:
- 编译阶段:用
$(BIN_PATH)/%.obj: %.c实现单个源文件到目标文件的映射,避免多目标冲突。 - 链接阶段:依赖所有编译生成的
.obj文件,生成未签名的.sys。 - 签名阶段:依赖未签名
.sys,生成最终签名产物,形成完整依赖链。
- 编译阶段:用
- 目录依赖处理:用
| $(BIN_PATH)的顺序依赖语法,确保编译/链接前先创建必要目录。 - 默认目标替换:将签名后的文件设为默认目标,无需指定
all,直接执行make即可触发全流程。 - 路径优化:去掉路径中的多余引号,避免Make解析路径时出现异常。
如果不需要区分签名前后的文件,可简化签名规则,直接覆盖原.sys:
$(FINAL_SYS): $(SRC_OBJ) | $(OUT_PATH) $(LNK) $(LFLAGS) $(LINCLUDE) $^ $(SGN) $@ .DEFAULT_GOAL := $(FINAL_SYS)
内容的提问来源于stack exchange,提问作者trapstar
相关产品推荐
相关产品推荐

