Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dependency on libgcc-dw2-1.dll from win32 executables. #29177

Merged
merged 10 commits into from
Nov 1, 2015

Conversation

vadimcn
Copy link
Contributor

@vadimcn vadimcn commented Oct 20, 2015

Note: for now, this change only affects -windows-gnu builds.

So why was this libgcc dylib dependency needed in the first place?
The stack unwinder needs to know about locations of unwind tables of all the modules loaded in the current process. The easiest portable way of achieving this is to have each module register itself with the unwinder when loaded into the process. All modules compiled by GCC do this by calling the __register_frame_info() in their startup code (that's crtbegin.o and crtend.o, which are automatically linked into any gcc output).
Another important piece is that there should be only one copy of the unwinder (and thus unwind tables registry) in the process. This pretty much means that the unwinder must be in a shared library (unless everything is statically linked).

Now, Rust compiler tries very hard to make sure that any given Rust crate appears in the final output just once. So if we link the unwinder statically to one of Rust's crates, everything should be fine.

Unfortunately, GCC startup objects are built under assumption that libgcc is the one true place for the unwind info registry, so I couldn't find any better way than to replace them. So out go crtbegin/crtend, in come rsbegin/rsend!

A side benefit of this change is that rustc is now more in control of the command line that goes to the linker, so we could stop using gcc as the linker driver and just invoke ld directly.

Rather than injecting a local `_Unwind_Resume` into the current translation unit,
just replace `resume` instruction with a direct call the the `eh_unwind_resume` lang item.
This is likely to be more robust in the face of future LLVM changes, and also allows us to delegate
work back to libgcc's `_Unwind_Resume`.
…our own (for now on for -windows-gnu target only).

Since it isn't possible to disable linkage of just GCC startup objects, we now need logic for finding libc installation directory and copying the required startup files (e.g. crt2.o) to rustlib directory.
Bonus change: use the `-nodefaultlibs` flag on Windows, thus paving the way to direct linker invocation.
@rust-highfive
Copy link
Collaborator

r? @arielb1

(rust_highfive has picked a reviewer for you, use r? to override)

@vadimcn
Copy link
Contributor Author

vadimcn commented Oct 20, 2015

r? @alexcrichton

@alexcrichton
Copy link
Member

@vadimcn you never cease to amaze me, fantastic work!

I'll try to get around to this tomorrow, and you may be met with a barrage of questions :)

"rsbegin.o".to_string(),
),
late_link_args: vec!(
"-lmingwex".to_string(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering, what do we rely on mingwex for anyway?

@vadimcn
Copy link
Contributor Author

vadimcn commented Oct 21, 2015

Looks better?

#[no_mangle]
#[link_section = ".eh_frame"]
// Marks beginning of the stack frame unwind info section
pub static __EH_FRAME_BEGIN__: [u8; 0] = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So just to clarify, this works because:

  • The linker will construct the .eh_frame section from the list of items contained within it on the command line. This is, however, guarantee to be the first, so &__EH_FRAME_BEGIN__ is guaranteed to be a pointer to the start of the section.
  • Similarly, rsend.o is guarnateed to be at the end, so it's guaranteed that &__EH_FRAME_END__ is at the end of the section.

Does that sound right? Also out of curiosity, what is the __EH_FRAME_END__ symbol used for? It's not explicitly referenced here, so is it referenced or dynamically loaded from the CRT or elsewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's my understanding of how this works.

Also out of curiosity, what is the EH_FRAME_END symbol used for?

It's used to support the weight of eh_frame section, thus the extra wide base.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm ok, and by "weight" and "extra wide base" do you mean how __EH_FRAME_END__ is a u32 while the start is a [u8; 0]? (a fact I just realized)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...the other reason is that you can't have a static variable (the zero footer) without a name (at least that I know of).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By extra wide base I meant the underscores :) just kidding :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh dear it looks like my ignorance is leaking, a zero footer makes sense to me!

@alexcrichton
Copy link
Member

cc @brson, just wanna give you a heads up about the we-are-building-our-own-object-files strategy. It seems fine to me but figured you may want to know!

@alexcrichton
Copy link
Member

This is basically r+ from me, but I think I'd prefer to hold this off until after the 1.5 beta is released so this lands in 1.6 instead (just so we have some extra time to weed out any problems here). Does that sound ok?

@vadimcn
Copy link
Contributor Author

vadimcn commented Oct 25, 2015

That's next Thursday, right? Fine by me.

@alexcrichton
Copy link
Member

OK cool, now I just wish I could bors r+ date=2015-...

@alexcrichton
Copy link
Member

@bors: r+ afc3046

@bors
Copy link
Contributor

bors commented Oct 28, 2015

⌛ Testing commit afc3046 with merge ccc9ff2...

@bors
Copy link
Contributor

bors commented Oct 28, 2015

💔 Test failed - auto-win-gnu-64-opt

@vadimcn
Copy link
Contributor Author

vadimcn commented Oct 29, 2015

Uh-oh, that's a stage0 ICE. Crashes in llvm::AttributeSet::removeAttributes

@vadimcn
Copy link
Contributor Author

vadimcn commented Nov 1, 2015

Looks like this was my own fault. The tricks I played with injection of the _Unwind_Resume symbol somehow caused it to turn into a "constant expression" value (but only on x86_64, which doesn't even use this symbol ), which did not sit well with LLVM functions like LLVMRemoveAttribute. Hiding it from stage0 resolves the issue.
r? @alexcrichton

@alexcrichton
Copy link
Member

@bors: r+ 0332ee9

@bors
Copy link
Contributor

bors commented Nov 1, 2015

⌛ Testing commit 0332ee9 with merge 7140918...

bors added a commit that referenced this pull request Nov 1, 2015
Note: for now, this change only affects `-windows-gnu` builds.

So why was this `libgcc` dylib dependency needed in the first place?
The stack unwinder needs to know about locations of unwind tables of all the modules loaded in the current process.  The easiest portable way of achieving this is to have each module register itself with the unwinder when loaded into the process.  All modules compiled by GCC do this by calling the __register_frame_info() in their startup code (that's `crtbegin.o` and `crtend.o`, which are automatically linked into any gcc output).
Another important piece is that there should be only one copy of the unwinder (and thus unwind tables registry) in the process.  This pretty much means that the unwinder must be in a shared library (unless everything is statically linked). 

Now, Rust compiler tries very hard to make sure that any given Rust crate appears in the final output just once.   So if we link the unwinder statically to one of Rust's crates, everything should be fine.

Unfortunately, GCC startup objects are built under assumption that `libgcc` is the one true place for the unwind info registry, so I couldn't find any better way than to replace them.  So out go `crtbegin`/`crtend`, in come `rsbegin`/`rsend`!  

A side benefit of this change is that rustc is now more in control of the command line that goes to the linker, so we could stop using `gcc` as the linker driver and just invoke `ld` directly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants