1. 引言
在完成Rust编译器包装器的修复后,我们遇到了编译过程最后阶段的链接问题。在Windows环境下,链接器无法正确找到llvm-pdbutil.exe和undname.exe这两个重要工具。本文将详细介绍如何解决这个问题。
2. 问题分析
2.1 错误表现
链接阶段报错找不到必要的工具:
- llvm-pdbutil.exe无法被正确定位
- undname.exe工具无法被找到和使用
FAILEC: gen/chrome/chrome_resource_allowlist.txt
C:/Users/ym/AppData/Local/Programs/Python/Python39/python.exe ../../tools/resources/generate_resource_allowlist.py -o gen/chrome/chrome_resource_allowlist.txt chrome.dll.pdb
Traceback (most recent call last):
File "C:\ungoogled-chromium-windows\build\src\tools\resources\generate_resource_allowlist.py", line 165, in <module>
main()
File "C:\ungoogled-chromium-windows\build\src\tools\resources\generate_resource_allowlist.py", line 161, in main
WriteResourceAllowlist(args)
File "C:\ungoogled-chromium-windows\build\src\tools\resources\generate_resource_allowlist.py", line 140, in WriteResourceAllowlist
resource_ids.update(func(input))
File "C:\ungoogled-chromium-windows\build\src\tools\resources\generate_resource_allowlist.py", line 61, in GetResourceAllowlistPDB
pdbutil = subprocess.Popen(
File "C:\Users\ym\AppData\Local\Programs\Python\Python39\lib\subprocess.py", line 951, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "C:\Users\ym\AppData\Local\Programs\Python\Python39\lib\subprocess.py", line 1420, in _execute_child
hp, ht, pid, tid = _winapi.CreateProcess(executable, args,
OSError: [WinError 193] %1 不是有效的 Win32 应用程序。
ninja: build stoppeC: subcommand failed.
C:\ungoogled-chromium-windows\build\src>exit
Traceback (most recent call last):
File "C:\ungoogled-chromium-windows\build.py", line 464, in <module>
main()
File "C:\ungoogled-chromium-windows\build.py", line 455, in main
_run_build_process('third_party\\ninja\\ninja.exe', '-C', 'out\\Default', 'chrome',
File "C:\ungoogled-chromium-windows\build.py", line 78, in _run_build_process
subprocess.run(('cmd.exe', '/k'),
File "C:\Users\ym\AppData\Local\Programs\Python\Python39\lib\subprocess.py", line 528, in run
raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '('cmd.exe', '/k')' returned non-zero exit status 1.
2.2 错误原因
- 工具路径解析逻辑未考虑操作系统差异
- 资源生成脚本中的工具调用方式不正确
3. 修复方案
3.1 修改generate_resource_allowlist.py
需要修改资源生成脚本中的工具调用逻辑:
# 修改前
pdbutil = subprocess.Popen(['llvm-pdbutil', 'dump', '-publics', path],
stdout=subprocess.PIPE)
# 修改后
pdbutil_exe = 'llvm-pdbutil.exe' if sys.platform == 'win32' else 'llvm-pdbutil'
pdbutil_path = os.path.join(llvm_bindir, pdbutil_exe)
pdbutil = subprocess.Popen([pdbutil_path, 'dump', '-publics', path],
stdout=subprocess.PIPE)
3.2 完善工具调用
对undname工具的调用也需要类似修改:
修改前
for name in names:
undname = subprocess.Popen(['undname', name],
stdout=subprocess.PIPE)
修改后
undname_exe = 'undname.exe' if sys.platform == 'win32' else 'undname'
for name in names:
undname = subprocess.Popen([undname_exe, name],
stdout=subprocess.PIPE)
3.3 完整脚本
#!/usr/bin/env python
# Copyright 2016 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
__doc__ = """generate_resource_allowlist.py [-o OUTPUT] INPUTS...
INPUTS are paths to unstripped binaries or PDBs containing references to
resources in their debug info.
This script generates a resource allowlist by reading debug info from
INPUTS and writes it to OUTPUT.
"""
# Allowlisted resources are identified by searching the input file for
# instantiations of the special function ui::AllowlistedResource (see
# ui/base/resource/allowlist.h).
import argparse
import os
import subprocess
import sys
import ar
llvm_bindir = os.path.join(os.path.dirname(sys.argv[0]), '..', '..',
'third_party', 'llvm-build', 'Release+Asserts',
'bin')
def ExtractAllowlistFromFile(path, resource_ids):
with open(path, 'rb') as f:
data = f.read()
# When symbol_level=0, only mangled names exist.
# E.g.: _ZN2ui19AllowlistedResourceILi22870EEEvv
prefix = b'AllowlistedResourceILi'
start_idx = 0
while start_idx != -1:
start_idx = data.find(prefix, start_idx)
if start_idx != -1:
end_idx = data.find(b'E', start_idx)
resource_ids.add(int(data[start_idx + len(prefix):end_idx]))
start_idx = end_idx
def GetResourceAllowlistELF(path):
# Produce a resource allowlist by searching for debug info referring to
# AllowlistedResource.
# This used to use "readelf -p .debug_str", but it doesn't seem to work with
# use_debug_fission=true. Reading the raw file is faster anyways.
resource_ids = set()
ExtractAllowlistFromFile(path, resource_ids)
return resource_ids
def GetResourceAllowlistPDB(path):
# Produce a resource allowlist by using llvm-pdbutil to read a PDB file's
# publics stream, which is essentially a symbol table, and searching for
# instantiations of AllowlistedResource. Any such instantiations are demangled
# to extract the resource identifier.
# 在 Windows 环境下添加 .exe 扩展名
pdbutil_exe = 'llvm-pdbutil.exe' if sys.platform == 'win32' else 'llvm-pdbutil'
pdbutil_path = os.path.join(llvm_bindir, pdbutil_exe)
pdbutil = subprocess.Popen(
[pdbutil_path, 'dump', '-publics', path],
stdout=subprocess.PIPE)
names = set()
for line in pdbutil.stdout:
line = line.decode('utf8')
# Read a line of the form
# "733352 | S_PUB32 [size = 56] `??$AllowlistedResource@$0BFGM@@ui@@YAXXZ`".
if '`' not in line:
continue
sym_name = line[line.find('`') + 1:line.rfind('`')]
# Under certain conditions such as the GN arg `use_clang_coverage = true` it
# is possible for the compiler to emit additional symbols that do not match
# the standard mangled-name format.
# Example: __profd_??$AllowlistedResource@$0BGPH@@ui@@YAXXZ
# C++ mangled names are supposed to begin with `?`, so check for that.
if 'AllowlistedResource' in sym_name and sym_name.startswith('?'):
names.add(sym_name)
exit_code = pdbutil.wait()
if exit_code != 0:
raise Exception('llvm-pdbutil exited with exit code %d' % exit_code)
resource_ids = set()
undname_exe = 'undname.exe' if sys.platform == 'win32' else 'undname'
for name in names:
undname = subprocess.Popen([undname_exe, name],
stdout=subprocess.PIPE)
found = False
for line in undname.stdout:
line = line.decode('utf8')
# Read a line of the form
# "void __cdecl ui::AllowlistedResource<5484>(void)".
prefix = ' ui::AllowlistedResource<'
pos = line.find(prefix)
if pos == -1:
continue
try:
resource_ids.add(int(line[pos + len(prefix):line.rfind('>')]))
except ValueError:
continue
found = True
break
exit_code = undname.wait()
if exit_code != 0:
raise Exception('llvm-undname exited with exit code %d' % exit_code)
if not found:
raise Exception('Unexpected undname output')
return resource_ids
def GetResourceAllowlistFileList(file_list_path):
# Creates a list of resources given the list of linker input files.
# Simply grep's them for AllowlistedResource<...>.
with open(file_list_path) as f:
paths = f.read().splitlines()
paths = ar.ExpandThinArchives(paths)
resource_ids = set()
for p in paths:
ExtractAllowlistFromFile(p, resource_ids)
return resource_ids
def WriteResourceAllowlist(args):
resource_ids = set()
for input in args.inputs:
with open(input, 'rb') as f:
magic = f.read(4)
chunk = f.read(60)
if magic == b'\x7fELF':
func = GetResourceAllowlistELF
elif magic == b'Micr':
func = GetResourceAllowlistPDB
elif magic == b'obj/' or b'/obj/' in chunk:
# For secondary toolchain, path will look like android_clang_arm/obj/...
func = GetResourceAllowlistFileList
else:
raise Exception('unknown file format')
resource_ids.update(func(input))
# The last time this broke, exactly two resources were still being found.
if len(resource_ids) < 100:
raise Exception('Suspiciously few resources found. Likely an issue with '
'the regular expression in this script. Found: ' +
','.join(sorted(resource_ids)))
for id in sorted(resource_ids):
args.output.write(str(id) + '\n')
def main():
parser = argparse.ArgumentParser(usage=__doc__)
parser.add_argument('inputs', nargs='+', help='An unstripped binary or PDB.')
parser.add_argument('-o',
dest='output',
type=argparse.FileType('w'),
default=sys.stdout,
help='The resource list path to write (default stdout)')
args = parser.parse_args()
WriteResourceAllowlist(args)
if __name__ == '__main__':
main()
4. 验证修复
4.1 测试步骤
- 修改generate_resource_allowlist.py文件
- 确保工具存在于正确路径
- 重新执行编译命令:
python build.py --tarball
4.2 验证要点
- 确认llvm-pdbutil.exe能被正确调用
- 验证undname.exe工具运行正常
- 检查资源生成过程完整性
5. 可能遇到的问题
5.1 工具路径问题
- 确保LLVM工具链安装完整
- 验证工具所在目录已添加到PATH
- 检查文件访问权限设置
5.2 环境配置问题
- 确认Python环境变量设置
- 验证Windows系统兼容性
- 检查构建脚本权限
6. 结语
通过本文的指导,我们成功解决了Ungoogled Chromium编译过程中最后链接阶段的工具调用问题。这个修复确保了编译过程能够顺利完成。
至此,我们已经解决了Chromium编译过程中的主要技术难点。在下一篇文章中,我们将验证编译完成的Ungoogled Chromium,探索其特性。请确保按本文的步骤正确修复链接工具的问题,为最终的编译成功奠定基础。
标签:Chromium127,resource,undname,Windows,Ungoogled,pdbutil,exe,path,line From: https://blog.csdn.net/qqyy_sj/article/details/144441455