This is an archive of the discontinued LLVM Phabricator instance.

[ASan][Darwin] Fix `TestCases/Darwin/init_for_dlopen.cpp` when running in a standalone build.
AbandonedPublic

Authored by delcypher on Apr 26 2021, 5:26 PM.

Details

Summary

When compiler-rt is configured for a standalone build the
config.compiler_rt_libdir path isn't correct because the Clang used
for testing won't use the ASan runtime from this path.

During testing it seems that this bug only actually matters on Darwin for
the init_for_dlopen.cpp test case. The test fails because
the %shared_libasan expands to a path that doesn't exist. It doesn't
exist in this particular scenario because the build hasn't been run.
Even if the build were run %shared_libasan would still point to the
wrong path.

To fix this we query clang during lit setup to find out where the
clang being used for testing will get its ASan runtime from instead
of using config.compiler_rt_libdir.

Although this change could be made for other platforms we only make
it for Darwin for now to avoid accidently breaking testing on other
platforms.

This change should have no effect for in tree builds (the common case)
because config.compiler_rt_libdir and the output for `clang
-print-file-name=lib` agree.

rdar://77182297O

Diff Detail

Event Timeline

delcypher requested review of this revision.Apr 26 2021, 5:26 PM
delcypher created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptApr 26 2021, 5:26 PM
Herald added a subscriber: Restricted Project. · View Herald Transcript

a couple nits, but otherwise lgtm. deferring to Julian for review of functionality

compiler-rt/test/asan/lit.cfg.py
57

how come this import is inside the function?

70

Is this python >= 3.6? What do you think about using f strings?

87

super nit picky, but what do you think about formatting this warning so it's easier to compare the two paths? something like

f"Path reported by clang != configured path\n\tclang path:\t{path}\n\tconfigured path:\t{config_crt_libdir_real}"
delcypher added inline comments.Apr 27 2021, 12:28 PM
compiler-rt/test/asan/lit.cfg.py
57

To use the import only when it's needed. I don't have strong opinions here so I can make the import global if that's what you'd prefer.

70

According to https://llvm.org/docs/GettingStarted.html#software python 3.6 is the minimum version so we should be able to use f strings.

87

I'll try it.

yln added a comment.Apr 27 2021, 2:51 PM

Would it be possible (and wouldn't it be better?) to set config.compiler_rt_libdir to the right path in the first place? This way we wouldn't get into the "path mismatch" state that we warn about.

I can see that compiler_rt_libdir is used in places (other than computing shared_libasan_path) as well.

First set by cmake compiler-rt/cmake/Modules/AddCompilerRT.cmake. Ideally, we would pick the right path there, right?

# Configure lit configuration files, including compiler-rt specific variables.
function(configure_compiler_rt_lit_site_cfg input output)
  set_llvm_build_mode()

  get_compiler_rt_output_dir(${COMPILER_RT_DEFAULT_TARGET_ARCH} output_dir)

  string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} COMPILER_RT_RESOLVED_TEST_COMPILER ${COMPILER_RT_TEST_COMPILER})
  string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} COMPILER_RT_RESOLVED_LIBRARY_OUTPUT_DIR ${output_dir})

  configure_lit_site_cfg(${input} ${output})
endfunction()

There also seems to be another config hook in compiler-rt/unittests/lit.common.unit.configured.in:

# LLVM tools dir and build mode can be passed in lit parameters,
# so try to apply substitution.
try:
  config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params
  config.compiler_rt_libdir = config.compiler_rt_libdir % lit_config.params
  config.llvm_build_mode = config.llvm_build_mode % lit_config.params
except KeyError as e:
  key, = e.args
  lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key))

Can we use or change one of these existing mechanisms?

Would it be possible (and wouldn't it be better?) to set config.compiler_rt_libdir to the right path in the first place? This way we wouldn't get into the "path mismatch" state that we warn about.

I did look into this. I didn't go down that route because it's problematic.

I can see that compiler_rt_libdir is used in places (other than computing shared_libasan_path) as well.

First set by cmake compiler-rt/cmake/Modules/AddCompilerRT.cmake. Ideally, we would pick the right path there, right?

# Configure lit configuration files, including compiler-rt specific variables.
function(configure_compiler_rt_lit_site_cfg input output)
  set_llvm_build_mode()

  get_compiler_rt_output_dir(${COMPILER_RT_DEFAULT_TARGET_ARCH} output_dir)

  string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} COMPILER_RT_RESOLVED_TEST_COMPILER ${COMPILER_RT_TEST_COMPILER})
  string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} COMPILER_RT_RESOLVED_LIBRARY_OUTPUT_DIR ${output_dir})

  configure_lit_site_cfg(${input} ${output})
endfunction()

So making the change at the CMake level has two problems.

  1. If you're testing an externally built toolchain with a standalone compiler-rt configure (what I'm doing) then you risk accidentally borking the toolchain if you invoke a build. If COMPILER_RT_RESOLVED_LIBRARY_OUTPUT_DIR points into the externally built toolchain's resource directory then building the Sanitizers would overwrite the libraries in the toolchain. That doesn't seem desirable. What happens right now if that you can do a build of the Sanitizer runtimes and they just end up in the build directory but won't actually used by testing. This isn't great either but it at least it isn't modifying an external toolchain.
  1. Changing this part of the CMake is incredibly risky because the way this code works differs between Apple and non-Apple platforms. Given that there's a much more targeted and lower risk option (i.e. this patch) I went for that instead.

There also seems to be another config hook in compiler-rt/unittests/lit.common.unit.configured.in:

# LLVM tools dir and build mode can be passed in lit parameters,
# so try to apply substitution.
try:
  config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params
  config.compiler_rt_libdir = config.compiler_rt_libdir % lit_config.params
  config.llvm_build_mode = config.llvm_build_mode % lit_config.params
except KeyError as e:
  key, = e.args
  lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key))

Can we use or change one of these existing mechanisms?

I don't think we can use that mechanism. Looking at it more I don't know how anyone is supposed to use that. In order for this to work the strings like config.llvm_tools_dir would need to contain python string interpolation keys (e.g. %(some_key_name)s). These strings don't contain this so I'm not sure how that code ever worked.

Thinking about this more a more general solution would be to modify config.compiler_rt_libdir from test/lit.common.cfg.py if we are doing a standalone build and we are building using clang. We might still run into problems here though because some people might expect the tests to use libraries built as part of the standalone compiler-rt build (the builtins tests expect this) rather than the libraries in the external toolchain's resource directory (the sanitizer tests expect this).

So unfortunately going down this path is going to be messy.

@yln Your comments have convinced me that I should try doing this a different way. I'll try the approach I outlined and I'll see how far I get.

yln added a comment.Apr 28 2021, 3:05 PM

@yln Your comments have convinced me that I should try doing this a different way. I'll try the approach I outlined and I'll see how far I get.

Thanks for considering other options. For the record: I don't know what this other path looks like. If it's not workable, then yes, let's move forward.
I just felt that "fixing up a path for a specific substitution" (when there are other uses) and the sense that we need a warning when the two config options diverged may give rise to future problems or confusion.

Changing this part of the CMake is incredibly risky because the way this code works differs between Apple and non-Apple platforms.

Maybe we can still guard the CMake change behind if (APPLE)?

delcypher abandoned this revision.Apr 30 2021, 5:18 PM

I'm abandoning this change. It is superseded by the much more general https://reviews.llvm.org/D101681