-
Notifications
You must be signed in to change notification settings - Fork 101
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
WIP raw_dylib: write the import .lib manually. #1414
Conversation
Thanks for working on this! |
src/archive.rs
Outdated
|
||
struct Writer { | ||
data: Vec<u8>, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can reuse the archive writer code of ar_archive_writer, right? This is already used by rustc, so adding extern crate ar_archive_writer;
to lib.rs should be enough, no need to add it as dependency to Cargo.toml.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seems to be a dearth of ar file writers on crates.io that support the Windows .lib symbol directory members (they need to be before the long names // member, which rules out ar crate for example), but I could have just missed it?
ar_archive_writer supports this. It is a direct port of LLVM's archive writer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, I did start by checking that crate but didn't take a second look after ar
didn't work out and I looked into the particulars of .libs, for some reason. It does panic on Coff, but that probably doesn't matter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The LLVM writer (and therefore presumably ar_archive_writer
) only writes the legacy linker member and not the modern one. However, msvc seems to be ok with this currently. It'd be great to write both but it's not strictly necessary.
src/archive.rs
Outdated
// ordinal_or_hint | ||
member.write_u16_le(0); | ||
// object_type | name_type = IMPORT_OBJECT_CODE | IMPORT_OBJECT_NAME | ||
member.write_u16_le(1 << 2 | 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The object crate has all those constants.
src/archive.rs
Outdated
.copy_from_slice(&member_offset.to_be_bytes()); | ||
member.data[current_member_table_offset + index * 4..][..4] | ||
.copy_from_slice(&member_offset.to_le_bytes()); | ||
// write import object: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this should be added to the object crate? It already has parser support for it.
Rustc's linker code is supposed to automatically add it: https://github.com/rust-lang/rust/blob/62270fb4d674fa109148c3a2618e4db9e03cd91c/compiler/rustc_codegen_ssa/src/back/link.rs#L2174-L2185 (and for rlibs: https://github.com/rust-lang/rust/blob/62270fb4d674fa109148c3a2618e4db9e03cd91c/compiler/rustc_codegen_ssa/src/back/link.rs#L395-L409) |
Yeah, I'm not sure what's going on there - it's definitely getting called and emitting files (I've dbg!()ed the output path and copied to desktop to check it does actually write the file...). My best guess is something is validating the file and filtering it out at some point, but nothing I could find after midnight 😅 |
This seems to actually work for me if using the LLVM linker ( The MSVC linker requires a bit more ceremony though:
As far as I'm aware the following symbols are required:
I think you can ignore everything else (e.g. |
src/archive.rs
Outdated
for dll_import in dll_imports { | ||
import_names.push(dll_import.name.as_str()); | ||
} | ||
let lib_path = tmpdir.join(format!("{}.lib", lib_name)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to be careful about lib name collision? I.e. should a random-ish id be added to the pathname to disambiguate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect rustc to merge all extern blocks with the same lib name. If that is indeed the case, there can't be any collisions as the tmpdir is unique per rustc invocation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough then!
…port descriptor object.
So I've got a bit more progress! Seems the sticking point for the imports being found was the default It was easy enough to provide a wrapper to get it working (in the sense of failing later at I took a stab at using
Seems the temp .lib won't actually appear on the final link commandline even when it's finding the exports.... I guess the relevant members get copied out or something? I'm not sure I'm following what I'm part way through implementing
I'm also missing I also tossed in the I'll try to fix the remaining bits to get the end-to-end going, including the other symbols, hopefully tomorrow, but there's a lot of cleanup to make this mergeable still! |
My test case was simpler, just an extern block with |
Probably gets rolled up into the I wanted to make sure I wasn't accidentally going to pull in an import from the stdlib, but I should be testing a few more combinations, especially no_std. |
The It might be easier to create a simpler sample lib using LLVM's utility (
Where "exports.def" is a def file containing what the DLL exports. |
More progress... but for some reason I've gotten it yelling at me that:
but only in this repo. If I pull the code out to a separate repo with an To clarify, |
Of course, I figure it out as soon as I pushed the broken code :) (although I still can't reproduce it outside the repo, which I don't like) It does crash on run though, which sounds like a problem for later. Probably just missing the thunk export. |
And break out code into dll_import_lib module.
This:
Unfortunately, while this code creates .libs that actually link and execute correctly when run outside this repo, it currently crashes on build when included when accessing some If so, the hack fix would be to add the |
Assuming all that is sorted out, I'd like to hear what your preference for where all the .lib writing code should go:
Plenty of cleanup work otherwise, still. |
I think at least writing the import object should definitely be in Using Still need to find a sec to catch up with the changes but generally I think the ideal would be to have as little here as necessary and outsource to crates that can be reused in other contexts. |
And fix lints.
Not much today, just fixing the unaligned access by copy-pasting the I'll poke the |
Blagh. Of course there's already an |
(That CI failure isn't something I did, right?) |
lld no longer looks at the archive symbol table as they found directly interning the symbol tables of the individual object files to avoid duplicate work and thus improve performance.
No, testing rand on FreeBSD is broken due to it's libm rounding something slightly incorrect. |
Well replacing my I'll leave that as is for now, and continue the other fixes. |
Added ordinals and I name-types, though I can't figure a good way to test them in Rust code other than it looking right to the equivalent This should probably test against MSVC compiled .dlls, I guess? I can't quite figure the testing setup in this repo, it seems to be basically just run all the files in |
If the |
Took a peek at windows-gnu: it's somewhat different output:
I think it's definitely doable, but probably out of scope for this PR. Hopefully gnullvm is pretty close to it too. |
I did expect it to match MSVC. LLVM doesn't have support for the format used by the GCC based MinGW. |
In any case, this is a problem that will solve itself... eventually. |
- Adds explicit checks for suported target - Pass in architecture/machine - Use a builder rather than a single function API for the library. - Use session errors rather than panicking at the top level.
Hmm - is it expected this should only work for
but maybe I'm doing something dumb. Should I be able to either cross-compile the stdlib or fetch a prebuilt one? Anyway, I think I'm closing in on this one: I want to do a sub-branch porting over to gimli-rs/object#595 which should clear out nearly all the coff file gunk in here if that lands before this; then it's pretty much just housekeeping stuff like checking for edge cases like 0xFFFF-ish numbers of imports; getting rid of a few I would love to get some tests in here for this, but there doesn't seem to be much infrastructure at the moment for that (which I get, if you can't use |
Arm windows isn't supported yet. |
All tests are defined in build_system/tests.rs. Tests can be arbitrary code. Just prefer avoiding invocation of a C compiler. As test I think you can compile a rust crate as cdylib, remove the .lib file and then depend on it as raw-dylib from an executable. Or alternatively depend on some system library as raw-dylib. |
Huh, weird: when I try to patch |
Sorry about that, there was a bug with writing the string table and test coverage wasn't as good as I thought. I've forced pushed a fix to that branch, and checked that I can run the cg_clif tests under Windows. |
Would that cause a build error in building std in cranelift though? Eg My best guess was the custom build scripts were somehow pulling in multiple builds of |
Yeah, the fix I pushed was enough to get |
Huh. I guess |
The error was nothing to do with this PR or import libraries; it would happen if you used that |
I added a simple build and run test to run the equivalent code to the Other than finishing the port onto the |
00f6500
to
cc26ee1
Compare
So gimli-rs/object#595 is merged but not published yet, so I don't want to update this branch to it just yet, but the updated branch is in a fairly decent shape now, and covers the remaining task list items: https://github.com/simonbuchan/rustc_codegen_cranelift/tree/raw_dylib_object_write. Now's the time for bikeshedding I guess? I'm not super happy about the Finally, the history here is a huge mess now (which I guess was going to be pretty obvious!), would you prefer this was re-opened with a more sensible history? The real meat of this is only one logical commit, really, especially with the actual |
object::read::coff::CoffFile<'data, &'data [u8], object::pe::ImageFileHeader>; | ||
let file = NtCoffFile::parse(buf).unwrap(); | ||
for symbol in file.symbols() { | ||
if symbol.is_definition() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if symbol.is_definition() { | |
if !symbol.is_undefined() && symbol.is_global() { |
I'm not completely sure this is what you need, but you definitely don't want is_definition
.
@simonbuchan What is the status of this PR? Do you plan on continuing work on this? Do you want a review from me? |
@bjorn3 dang, I totally missed this comment, sorry! I think I'm theory I was waiting for I expect this is mostly redundant with rust-lang/ar_archive_writer#15 now? The code would be quite different either way, so really this specific PR should close, but if it isn't conflicting with other people I'm happy to try picking this back up. |
No worries. I've got a branch to use the import library writing support of ar_archive_writer at https://github.com/rust-lang/rustc_codegen_cranelift/tree/import_libs (and rust-lang/rust#128206 for deduplicating most of this code with cg_llvm), but it is still blocked on using |
@dpaoliello seems to be working on implementing the rest of the necessary changes. |
Yep, was just trying to figure out how this was fitting together, and saw that PR on |
Yep, I have a prototype of raw-dylib working in cranelift using the new features in |
Implements #1345.
Currently succeeds building and running the
windows-sys
cratesenum-windows
example withRUSTFLAGS=--cfg windows_raw_dylib
.TODO (in no particular order):
ar_archive_writer
object::write
- see Upstreaming some archive / coff short import writing code? gimli-rs/object#591 (comment)#[link(import_name_type)
See what's needed for windows-gnu target - it might be in scope?looks out of scope: WIP raw_dylib: write the import .lib manually. #1414 (comment)Implement machine typesseems like only x86_64 is supported for windows in cranelift right now?as
casts for explicittry_into()
checks (with.expect()
where appropriate).