This is MSVC's behaviour. LLD was matching it before D99078. Let's go back this way.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
lld/COFF/DriverUtils.cpp | ||
---|---|---|
109–110 | Since ehcont implies cf, perhaps in this case it should also imply longjmp? |
lld/COFF/DriverUtils.cpp | ||
---|---|---|
109–110 | No idea. But if we make ehcont imply longjmp, an option like -guard:cf,nolongjmp,ehcont won't disable nolongjmp. So it should align with MSVC too. Could you help to have a check? Thanks! |
lld/COFF/DriverUtils.cpp | ||
---|---|---|
109–110 | I think to align with MSVC we'd have to change the code structure to process negation options after positive options, rather than updating the feature mask in order. We would also need to check the semantics of /guard:longjump,nolonjump / /guard:nolongump,longjump, those probably become warnings or errors. Attention to detail like this is appreciated, but I'd also approve this change as is to get us back to where we were. |
lld/COFF/DriverUtils.cpp | ||
---|---|---|
109–110 | Yeah. Option consistency is important, however, an explicit or implicit confliction option is in the grey area. |
I did a non-exhaustive test with clang++ ehcont_guard_msvc.cpp -o ehcont_guard_msvc.exe -target x86_64-pc-windows-msvc -Xclang -ehcontguard -O -Xlinker /guard:xxxxxx, using MSVC Incremental Linker Version 14.29.30146.0, then checked using llvm-readobj --coff-load-config ehcont_guard_msvc.exe.
The result is a bit unexpected:
link flag | guard fid | guard longjmp | guard ehcont |
---|---|---|---|
(no /guard) | no | no | no |
/guard:no | no | no | no |
/guard:cf | yes | yes | no |
/guard:no,cf | yes | yes | no |
/guard:longjmp | yes | no | no |
/guard:cf,longjmp | yes | yes | no |
/guard:cf,nolongjmp | yes | no | no |
/guard:nolongjmp,cf | yes | yes | no |
/guard:ehcont | yes | no | yes |
/guard:cf,ehcont | yes | yes | yes |
/guard;cf,nolongjmp,ehcont | yes | no | yes |
/guard:cf,longjmp,ehcont | yes | yes | yes |
/guard:longjmp,ehcont | yes | no | yes |
/guard:cf,longjmp,ehcont,no | no | no | no |
/guard:no,cf,longjmp,ehcont | yes | yes | yes |
- Options are parsed in order, negation does not override later options.
- longjmp does not actually enable guard long jump (might be a bug?)
- ehguard does indeed not enable guard long jump.
This is the code I used:
#include <cstdio> #include <setjmp.h> struct my_exception_t { int code; }; __attribute__((noinline)) void do_test(bool should_throw) { printf("Starting test, should_throw = %d\n", should_throw); try { if (should_throw) { throw my_exception_t { 1234 }; } puts("Exception not thrown."); } catch (my_exception_t ex) { printf("Caught exception with value %d.\n", ex.code); } } int main(int argc, char *argv[]) { if (argc > 1) { do_test(true); } else { do_test(false); } jmp_buf my_jmp_buf; jmp_buf my_jmp_buf2; if (setjmp(my_jmp_buf) > 0) { puts("Longjmp target reached."); return 0; } puts("Performing longjmp..."); longjmp(my_jmp_buf2, 1); puts("Unexpectedly returned from longjmp!"); return 0; }
Great job! Thanks @alvinhochun !
If we consider the longjmp not enabling long jump is a bug, and fix the table accordingly, then it is exactly what LLD is doing with this patch. It looks to me MSVC handles the options in order too. Anyway, it matches my understanding in options parsing.
@alvinhochun Does this patch seem ok to you in this form, or do you think it would make sense to match link.exe's behaviour wrt longjmp more exactly?
I think this patch in its current form is probably fine but honestly I don't know. It would be nice to have a second opinion from someone who actually use the MSVC toolchain.
I mentioned earlier I think this is fine as is, so I'll go ahead and mark it so in Phab.
Since ehcont implies cf, perhaps in this case it should also imply longjmp?