implib-0.3.3/.cargo_vcs_info.json0000644000000001360000000000100123130ustar { "git": { "sha1": "490aef9df3d74d787b132551d689a86a2e4084c9" }, "path_in_vcs": "" }implib-0.3.3/.github/dependabot.yml000064400000000000000000000012341046102023000152730ustar 00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "cargo" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "monthly" groups: crates-io: patterns: - "*" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" implib-0.3.3/.github/workflows/CI.yml000064400000000000000000000062001046102023000155140ustar 00000000000000on: [push, pull_request] name: CI jobs: check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: check test: name: Test Suite runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - name: Cache cargo build uses: Swatinem/rust-cache@v2 - uses: actions-rs/cargo@v1 with: command: test - name: Install LLVM if: matrix.os == 'ubuntu-latest' run: sudo apt-get install -y llvm - name: Install LLVM if: matrix.os == 'macos-latest' run: | brew install llvm echo "$(brew --prefix llvm)/bin" >> $GITHUB_PATH - name: Run llvm-dlltool run: | llvm-dlltool -m i386:x86-64 -d tests/python39.def -l llvm-amd64-python39.lib llvm-dlltool -m i386 -d tests/python39.def -l llvm-i386-python39.lib if: matrix.os != 'windows-latest' - name: Compare with llvm-dlltool if: matrix.os != 'windows-latest' shell: bash run: | set -e if cmp -s -- amd64-python39.lib llvm-amd64-python39.lib; then echo "Success!"; else echo "Fail!"; exit 1; fi if cmp -s -- i386-python39.lib llvm-i386-python39.lib; then echo "Success!"; else echo "Fail!"; exit 1; fi fuzz: name: Fuzz Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: nightly override: true - name: Cache cargo build uses: Swatinem/rust-cache@v2 - name: Cache fuzz corpus - Step 1 uses: actions/cache@v4 with: path: fuzz/corpus key: ${{ runner.os }}-fuzz-corpus - name: Cache fuzz corpus - Step 2 uses: actions/cache@v4 with: path: fuzz/corpus key: ${{ runner.os }}-fuzz-corpus-${{ hashFiles('fuzz/corpus/**/*') }} restore-keys: | ${{ runner.os }}-fuzz-corpus-${{ hashFiles('fuzz/corpus/**/*') }} ${{ runner.os }}-fuzz-corpus- - uses: dtolnay/install@master with: crate: cargo-fuzz - name: cargo fuzz run: cargo fuzz run fuzz_implib -- -runs=100000 -max_len=10485760 -dict=fuzz/dict.txt fmt: name: Rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add rustfmt - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check implib-0.3.3/.gitignore000064400000000000000000000000531046102023000130710ustar 00000000000000/target /Cargo.lock *.lib *.dll.a *.o /tmp implib-0.3.3/Cargo.toml0000644000000021270000000000100103130ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "implib" version = "0.3.3" build = false autobins = false autoexamples = false autotests = false autobenches = false description = "Generate Windows import library from module definition file" readme = "README.md" license = "MIT" repository = "https://github.com/messense/implib-rs" [lib] name = "implib" path = "src/lib.rs" [[test]] name = "test_import_lib" path = "tests/test_import_lib.rs" [dependencies.memoffset] version = "0.9.0" [dependencies.object] version = "0.36.4" features = ["pe"] default-features = false [features] default = [ "msvc", "gnu", ] gnu = ["object/write_std"] msvc = [] implib-0.3.3/Cargo.toml.orig000064400000000000000000000010011046102023000137620ustar 00000000000000[package] name = "implib" version = "0.3.3" description = "Generate Windows import library from module definition file" edition = "2021" license = "MIT" repository = "https://github.com/messense/implib-rs" readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] memoffset = "0.9.0" object = { version = "0.36.4", default-features = false, features = ["pe"] } [features] default = ["msvc", "gnu"] msvc = [] gnu = ["object/write_std"] implib-0.3.3/LICENSE000064400000000000000000000020761046102023000121150ustar 00000000000000The MIT License (MIT) Copyright (c) 2022-present Messense Lv Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. implib-0.3.3/README.md000064400000000000000000000011301046102023000123550ustar 00000000000000# implib-rs [![CI](https://github.com/messense/implib-rs/workflows/CI/badge.svg)](https://github.com/messense/implib-rs/actions?query=workflow%3ACI) [![Crates.io](https://img.shields.io/crates/v/implib.svg)](https://crates.io/crates/implib) [![docs.rs](https://docs.rs/implib/badge.svg)](https://docs.rs/implib/) Generate Windows import library from module definition file in Rust. ## Installation Add it to your `Cargo.toml`: ```toml [dependencies] implib = "0.3" ``` ## License This work is released under the MIT license. A copy of the license is provided in the [LICENSE](./LICENSE) file. implib-0.3.3/src/ar/LICENSE000064400000000000000000000020611046102023000133000ustar 00000000000000MIT License Copyright (c) 2017 Matthew D. Steele Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.implib-0.3.3/src/ar/error.rs000064400000000000000000000017041046102023000137750ustar 00000000000000//! Basic error handling macros to remove boilerplate - not for public export /// Simple macro to produce an error in the format this crate typically produces /// /// This is a simplification on `io::Error` #[macro_export] #[doc(hidden)] macro_rules! err { ($fmt: expr, $($arg:tt)*) => ( std::io::Error::new(std::io::ErrorKind::InvalidInput, format!($fmt, $($arg)*)) ); ($msg: literal) => ( Error::new(ErrorKind::InvalidInput, $msg) ); } /// Macro to check a precondition, and if the invariant does not hold return an error #[macro_export] #[doc(hidden)] macro_rules! ensure { ($expr: expr, $msg: literal $(,)?) => ( if !$expr { bail!($msg); } ); ($expr: expr, $fmt: expr, $($arg:tt)*) => ( if !$expr { bail!($fmt, $($arg)*); } ); } /// Unconditionally return an error #[macro_export] #[doc(hidden)] macro_rules! bail { ($fmt: expr, $($arg:tt)*) => ( return Err(err!($fmt, $($arg)*)) ); ($msg: literal) => ( return Err(err!($msg)); ); } implib-0.3.3/src/ar/mod.rs000064400000000000000000000053051046102023000134240ustar 00000000000000//! A library for encoding/decoding Unix archive files. //! //! This library provides utilities necessary to manage [Unix archive //! files](https://en.wikipedia.org/wiki/Ar_(Unix)) (as generated by the //! standard `ar` command line utility) abstracted over a reader or writer. //! This library provides a streaming interface that avoids having to ever load //! a full archive entry into memory. //! //! The API of this crate is meant to be similar to that of the //! [`tar`](https://crates.io/crates/tar) crate. //! mod error; mod write; use crate::{bail, ensure, err}; pub use write::GnuBuilder; // ========================================================================= // const GLOBAL_HEADER_LEN: usize = 8; const GLOBAL_HEADER: &[u8; GLOBAL_HEADER_LEN] = b"!\n"; const GNU_NAME_TABLE_ID: &str = "//"; const GNU_SYMBOL_LOOKUP_TABLE_ID: &str = "/"; // ========================================================================= // /// Representation of an archive entry header. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Header { identifier: Vec, mtime: u64, uid: u32, gid: u32, mode: u32, size: u64, } impl Header { /// Creates a header with the given file identifier and size, and all /// other fields set to zero. pub fn new(identifier: Vec, size: u64) -> Header { Header { identifier, mtime: 0, uid: 0, gid: 0, mode: 0o644, size, } } /// Returns the file identifier. pub fn identifier(&self) -> &[u8] { &self.identifier } /// Sets the mode bits for this file. pub fn set_mode(&mut self, mode: u32) { self.mode = mode; } /// Returns the length of the file, in bytes. pub fn size(&self) -> u64 { self.size } /// Validates the header is somewhat sane against the specification. pub fn validate(&self) -> std::io::Result<()> { ensure!( num_digits(self.mtime, 10) <= 12, "MTime `{}` > 12 digits", self.mtime ); ensure!( num_digits(self.uid, 10) <= 6, "UID `{}` > 6 digits", self.uid ); ensure!( num_digits(self.gid, 10) <= 6, "GID `{}` > 6 digits", self.gid ); ensure!( num_digits(self.mode, 8) <= 8, "Mode `{:o}` > 8 octal digits", self.mode ); Ok(()) } } #[inline] fn num_digits>(val: N, radix: u64) -> u64 { let mut val = val.into(); if val == 0 { return 1; } let mut digits = 0; while val != 0 { val /= radix; digits += 1; } digits } implib-0.3.3/src/ar/write.rs000064400000000000000000000312131046102023000137740ustar 00000000000000#![allow(clippy::write_with_newline)] use std::collections::{HashMap, HashSet}; use std::convert::TryFrom; use std::io::{self, Error, ErrorKind, Read, Result, Seek, Write}; use super::*; use crate::{bail, ensure, err}; // ========================================================================= // impl Header { fn write_gnu( &self, deterministic: bool, writer: &mut W, names: &HashMap, usize>, ) -> Result<()> where W: Write, { self.validate()?; if self.identifier.len() > 15 { let offset = names[&self.identifier]; write!(writer, "/{:<15}", offset)?; } else { writer.write_all(&self.identifier)?; writer.write_all(b"/")?; writer.write_all(&vec![b' '; 15 - self.identifier.len()])?; } if deterministic { write!( writer, "{:<12}{:<6}{:<6}{:<8o}{:<10}`\n", 0, 0, 0, 0o644, self.size )?; } else { write!( writer, "{:<12}{:<6}{:<6}{:<8o}{:<10}`\n", self.mtime, self.uid, self.gid, self.mode, self.size )?; } Ok(()) } } // ========================================================================= // /// Builder for GNU archive format /// /// # TL;DR /// The GNU format is a backwards incompatible archive format that diverges from the legacy Unix /// archive format in the following significant ways: /// /// 1) It can contain a binary symbol table that needs to be the first member of the archive. /// This table can contain either 32bit or 64bit offsets pointing to the entities that symbols /// relate to. /// /// Unlike the BSD tables the GNU tables are _somewhat_ more formally defined and are simpler in /// construction. /// /// 2) The handling of extended strings is done with a string lookup table (either as the first of /// second member) which is little more than a large string array. /// /// 3) Extensions exist to create a rare format known as a thin-archive. /// /// 4) GNU archives have a formal [deterministic mode](#deterministic-archives) that is important /// for build systems and toolchains. /// /// Most tools outside of BSD targets tend to use GNU format as the defacto standard, and it is /// well-supported by LLVM and GNU toolchains. More subtle variants of this format exist such as /// the unimplemented Microsoft extended ECOFF archive. /// /// # Layout /// Except where indicated, the metadata for the archive is typically encoded as ascii strings. All /// ascii strings in an archive are padded to the length of the given field with ascii space `0x20` /// as the fill value. This gives an archive a general fixed format look if opened in a text /// editor. /// /// Data is emplaced inline directly after a header record, no manipulations are done on data /// stored in an archive, and there are no restrictions on what data can be stored in an archive. /// Data might have a padding character (`\n`) added if the entity would be on an odd byte /// boundary, but this is purely an internal detail of the format and not visible in any metadata. /// /// **Header** /// /// | Section | Type | /// |-----------------|---------------------| /// | Magic signature | Literal `!\n` | /// /// **Entity Header** /// /// | Section | Type | Notes | /// |---------|----------------|--------------------------------------------------------------------------------------------------| /// | Name | `[u8; 16]` | Gnu handles strings in a manner that _effectively_ reduces this to 15 bytes | /// | MTime | `[u8; 12]` | Seconds since the Unix epoch. Often `0` as per [deterministic archives](#deterministic-archives) | /// | Uid | `[u8; 6]` | Unix plain user id. Often `0` as per [deterministic archives](#deterministic-archives) | /// | Gid | `[u8; 6]` | Unix plain group id. Often `0` as per [deterministic archives](#deterministic-archives) | /// | Mode | `[u8; 8]` | Unix file mode in Octal. Often `0` as per [deterministic archives](#deterministic-archives) | /// | Size | `[u8; 10]` | Entity data size in bytes, the size _does not reflect_ any padding | /// | End | Literal `\`\n` | Marks the end of the entity header | /// /// **Symbol table (if present)** /// /// Symbol tables are prepended with an entity header, although most implementations choose to make /// the header all spaces in contrast to general header format for [deterministic /// archives](#Deterministic Archives) but with the same general effect. /// /// The name for the entity for the symbol table is either `//` or `/SYM64/` dependent on if the /// overall size of the archive crosses the maximum addressable size allowed by 32 bits. /// /// | Section | Type | Notes | /// |----------|-------------------|---------------------------------------------------------| /// | Num syms | `u32` / `u64` | _Generally_ `u32` but can be `u64` for > 4Gb archives | /// | Offsets | `[u32]` / `[u64]` | Pointer from a symbol to the relevant archive entity | /// | Names | `[c_str]` | The name of each symbol as a plain C style string array | /// /// **Extended strings (if present)** /// /// GNU archives generally encode names inline in the format `/some_name.o/`. /// /// The bracketed `/` pairing allows GNU archives to contain embedded spaces and other metachars /// (excluding `/` itself). /// /// If the name is _greater than_ 15 bytes it is encoded as offset number into a string table. The /// string table is one of the first few members in the archive and is given as strings separated /// by the byte sequence `[0x2F, 0x0A]` (or `\\/n` in ascii). /// No padding is done in the string table itself and the offset written to the entity header is zero /// based from the start of the string table. /// /// The entity name for the string table is formatted as `/#offset`, for example, for an extended /// name starting at offset `4853` the value written to the entity header becomes `/#4853` /// /// ## Deterministic Archives /// The existence of several variables in entity headers make the format poorly suited to /// consistent generation of archives. This confuses toolchains which may interpret frequently /// changing headers as a change to the overall archive and force needless recomputations. /// /// As such, a backwards compatible extension exists for GNU archives where all variable fields not /// directly related to an entities data are set to ascii `0`. This is known as deterministic mode /// and is common for most modern in use unix archives (the format has long since lost its original /// duty as a general archive format and is now mostly used for toolchain operations). pub struct GnuBuilder { writer: W, deterministic: bool, short_names: HashSet>, long_names: HashMap, usize>, symbol_table_relocations: Vec>, symbol_index: usize, } impl GnuBuilder { /// The third argument is a map from file identifier to the name of all symbols in the file. /// Create a new archive builder with the underlying writer object as the /// destination of all data written. The `identifiers` parameter must give /// the complete list of entry identifiers that will be included in this /// archive. The last argument is a map from file identifier to the name of /// all symbols in the file. pub fn new_with_symbol_table( mut writer: W, deterministic: bool, identifiers: Vec>, symbol_table: Vec>>, ) -> Result> { let mut short_names = HashSet::>::new(); let mut long_names = HashMap::, usize>::new(); let mut name_table_size: usize = 0; for identifier in identifiers.into_iter() { let length = identifier.len(); if length > 15 { long_names.insert(identifier, name_table_size); name_table_size += length + 2; } else { short_names.insert(identifier); } } let name_table_needs_padding = name_table_size % 2 != 0; if name_table_needs_padding { name_table_size += 3; // ` /\n` } writer.write_all(GLOBAL_HEADER)?; let mut symbol_table_relocations: Vec> = Vec::with_capacity(symbol_table.len()); if !symbol_table.is_empty() { let wordsize = std::mem::size_of::(); let symbol_count: usize = symbol_table.iter().map(|symbols| symbols.len()).sum(); let symbols = symbol_table.iter().flatten(); let mut symbol_table_size: usize = wordsize + wordsize * symbol_count + symbols.map(|symbol| symbol.len() + 1).sum::(); let symbol_table_needs_padding = symbol_table_size % 2 != 0; if symbol_table_needs_padding { symbol_table_size += 3; // ` /\n` } write!( writer, "{:<16}{:<12}{:<6}{:<6}{:<8o}{:<10}`\n", GNU_SYMBOL_LOOKUP_TABLE_ID, 0, 0, 0, 0, symbol_table_size )?; writer.write_all(&u32::to_be_bytes(u32::try_from(symbol_count).map_err( |_| err!("Too many symbols for 32bit table `{}`", symbol_count), )?))?; for symbols in &symbol_table { let mut sym_rels = Vec::new(); for _symbol in symbols { sym_rels.push(writer.stream_position()?); writer.write_all(&u32::to_be_bytes(0xcafebabe))?; } symbol_table_relocations.push(sym_rels); } for symbol in symbol_table.iter().flatten() { writer.write_all(symbol)?; writer.write_all(b"\0")?; } if symbol_table_needs_padding { writer.write_all(b" /\n")?; } } if !long_names.is_empty() { write!( writer, "{:<48}{:<10}`\n", GNU_NAME_TABLE_ID, name_table_size )?; let mut entries: Vec<(usize, &[u8])> = long_names .iter() .map(|(id, &start)| (start, id.as_slice())) .collect(); entries.sort(); for (_, id) in entries { writer.write_all(id)?; writer.write_all(b"/\n")?; } if name_table_needs_padding { writer.write_all(b" /\n")?; } } Ok(GnuBuilder { writer, deterministic, short_names, long_names, symbol_table_relocations, symbol_index: 0, }) } /// Adds a new entry to this archive. pub fn append(&mut self, header: &Header, mut data: R) -> Result<()> { let is_long_name = header.identifier().len() > 15; let has_name = if is_long_name { self.long_names.contains_key(header.identifier()) } else { self.short_names.contains(header.identifier()) }; ensure!( has_name, "Identifier `{:?}` was not in the list of identifiers passed to GnuBuilder::new()", String::from_utf8_lossy(header.identifier()) ); if let Some(relocs) = self.symbol_table_relocations.get(self.symbol_index) { let entry_offset = self.writer.stream_position()?; let entry_offset_bytes = u32::to_be_bytes( u32::try_from(entry_offset).map_err(|_| err!("Archive larger than 4GB"))?, ); for &reloc_offset in relocs { self.writer.seek(io::SeekFrom::Start(reloc_offset))?; self.writer.write_all(&entry_offset_bytes)?; } self.writer.seek(io::SeekFrom::Start(entry_offset))?; self.symbol_index += 1; } header.write_gnu(self.deterministic, &mut self.writer, &self.long_names)?; let actual_size = io::copy(&mut data, &mut self.writer)?; if actual_size != header.size() { bail!( "Wrong file size (header.size() = `{}`, actual = `{}`)", header.size(), actual_size ); } if actual_size % 2 != 0 { self.writer.write_all(&[b'\n'])?; } Ok(()) } } implib-0.3.3/src/def/mod.rs000064400000000000000000000026331046102023000135610ustar 00000000000000use std::io::Error; use self::parser::Parser; use crate::MachineType; mod parser; /// Simple .DEF file parser #[derive(Debug, Clone, Default)] pub struct ModuleDef { pub exports: Vec, pub import_name: String, pub image_base: u64, pub stack_reserve: u64, pub stack_commit: u64, pub heap_reserve: u64, pub heap_commit: u64, pub major_image_version: u32, pub minor_image_version: u32, pub major_os_version: u32, pub minor_os_version: u32, } impl ModuleDef { pub fn parse(def: &str, machine: MachineType) -> Result { Parser::new(def, machine).parse() } } /// COFF short export #[derive(Debug, Clone, Default)] pub struct ShortExport { /// The name of the export as specified in the .def file or on the command /// line, i.e. "foo" in "/EXPORT:foo", and "bar" in "/EXPORT:foo=bar" pub name: String, /// The external, exported name. Only non-empty when export renaming is in /// effect, i.e. "foo" in "/EXPORT:foo=bar". pub ext_name: Option, /// The real, mangled symbol name from the object file. pub symbol_name: String, /// Creates a weak alias. This is the name of the weak aliasee. In a .def /// file, this is "baz" in "EXPORTS\nfoo = bar == baz". pub alias_target: String, pub ordinal: u16, pub no_name: bool, pub data: bool, pub private: bool, pub constant: bool, } implib-0.3.3/src/def/parser.rs000064400000000000000000000425761046102023000143100ustar 00000000000000use std::io::{Error, ErrorKind}; use std::iter::Peekable; use std::str::CharIndices; use super::{ModuleDef, ShortExport}; use crate::MachineType; type Result = std::result::Result; #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum TokenKind { Unknown, Eof, Identifier, Comma, Equal, EqualEqual, KwBase, KwConstant, KwData, KwExports, KwHeapsize, KwLibrary, KwName, KwNoname, KwPrivate, KwStacksize, KwVersion, } impl Default for TokenKind { fn default() -> Self { Self::Unknown } } #[derive(Debug, Clone, PartialEq, Eq)] struct Token<'a> { kind: TokenKind, value: Option<&'a str>, } impl<'a> Token<'a> { fn unwrap_value(&self) -> &'a str { self.value.expect("token value missing") } } #[derive(Debug)] struct Lexer<'a> { text: &'a str, chars: Peekable>, } impl<'a> Lexer<'a> { pub fn new(text: &'a str) -> Self { Lexer { text, chars: text.char_indices().peekable(), } } } impl<'a> Iterator for Lexer<'a> { type Item = Token<'a>; fn next(&mut self) -> Option { if let Some((i, c)) = self.chars.next() { match c { '\0' => Some(Token { kind: TokenKind::Eof, value: None, }), ';' => { for (_, next_c) in self.chars.by_ref() { if next_c == '\n' { break; } } self.next() } '=' => match self.chars.next_if(|&x| x.1 == '=').map(|x| x.1) { Some(_) => Some(Token { kind: TokenKind::EqualEqual, value: Some("=="), }), None => Some(Token { kind: TokenKind::Equal, value: Some("="), }), }, ',' => Some(Token { kind: TokenKind::Comma, value: Some(","), }), '"' => { let mut end = i + 1; for (j, next_c) in self.chars.by_ref() { if next_c == '"' { end = j; break; } } Some(Token { kind: TokenKind::Identifier, value: Some(self.text[i + 1..end].trim()), }) } _ => { let mut end = i; for (j, next_c) in self.chars.by_ref() { match next_c { '=' | ',' | ';' | '\r' | '\n' | ' ' | '\t' | '\x0B' => { end = j; break; } _ => { end = j + next_c.len_utf8(); } } } let word = self.text[i..end].trim(); if word.is_empty() { self.next() } else { let kind = match word { "BASE" => TokenKind::KwBase, "CONSTANT" => TokenKind::KwConstant, "DATA" => TokenKind::KwData, "EXPORTS" => TokenKind::KwExports, "HEAPSIZE" => TokenKind::KwHeapsize, "LIBRARY" => TokenKind::KwLibrary, "NAME" => TokenKind::KwName, "NONAME" => TokenKind::KwNoname, "PRIVATE" => TokenKind::KwPrivate, "STACKSIZE" => TokenKind::KwStacksize, "VERSION" => TokenKind::KwVersion, _ => TokenKind::Identifier, }; Some(Token { kind, value: Some(word), }) } } } } else { Some(Token { kind: TokenKind::Eof, value: None, }) } } } #[derive(Debug)] pub struct Parser<'a> { lexer: Lexer<'a>, stack: Vec>, def: ModuleDef, machine: MachineType, } impl<'a> Parser<'a> { pub fn new(text: &'a str, machine: MachineType) -> Self { Parser { lexer: Lexer::new(text), stack: Vec::new(), def: ModuleDef::default(), machine, } } pub fn parse(mut self) -> Result { loop { let eof = self.parse_one()?; if eof { break; } } Ok(self.def) } fn parse_one(&mut self) -> Result { let token = self.read(); match token.kind { TokenKind::Eof => return Ok(true), TokenKind::KwExports => loop { let next = self.read(); if next.kind != TokenKind::Identifier { self.stack.push(next); return Ok(false); } self.parse_export(next)?; }, TokenKind::KwHeapsize => { let (reserve, commit) = self.parse_numbers()?; self.def.heap_reserve = reserve; self.def.heap_commit = commit; } TokenKind::KwStacksize => { let (reserve, commit) = self.parse_numbers()?; self.def.stack_reserve = reserve; self.def.stack_commit = commit; } TokenKind::KwLibrary | TokenKind::KwName => { let (name, image_base) = self.parse_name()?; self.def.import_name = name; self.def.image_base = image_base; } TokenKind::KwVersion => { let (major, minor) = self.parse_version()?; self.def.major_image_version = major; self.def.minor_image_version = minor; } _ => { return Err(Error::new( ErrorKind::InvalidInput, format!("unknown directive: {}", token.unwrap_value()), )) } } Ok(false) } fn parse_export(&mut self, token: Token<'a>) -> Result<()> { let mut export = ShortExport { name: token.unwrap_value().to_string(), ..Default::default() }; let token = self.read(); if token.kind == TokenKind::Equal { let token = self.read(); if token.kind != TokenKind::Identifier { return Err(Error::new( ErrorKind::InvalidInput, format!("expected identifier, found: {:?}", token.kind), )); } export.ext_name = Some(export.name); export.name = token.unwrap_value().to_string(); } else { self.stack.push(token); } if self.machine == MachineType::I386 { if !is_decorated(&export.name) { export.name = format!("_{}", export.name); } if let Some(ext_name) = export.ext_name.as_ref() { if !is_decorated(ext_name) { export.ext_name = Some(format!("_{}", ext_name)); } } } loop { let token = self.read(); if token.kind == TokenKind::Identifier && token.value.map(|v| v.starts_with('@')).unwrap_or_default() { let value = token.unwrap_value(); if value == "@" { // "foo @ 10" let token = self.read(); if let Some(value) = token.value { match value.parse() { Ok(ordinal) => export.ordinal = ordinal, Err(_) => { return Err(Error::new( ErrorKind::InvalidInput, format!("invalid ordinal: {}", value), )) } } } else { return Err(Error::new( ErrorKind::InvalidInput, format!("expected identifier, found: {:?}", token.kind), )); } } else if value[1..].parse::().is_err() { // "foo \n @bar" - Not an ordinal modifier at all, but the next // export (fastcall decorated) - complete the current one. self.stack.push(token); self.def.exports.push(export); return Ok(()); } // "foo @10" let token = self.read(); if token.kind == TokenKind::KwNoname { export.no_name = true; } else { self.stack.push(token); } continue; } match token.kind { TokenKind::KwData => { export.data = true; } TokenKind::KwConstant => { export.constant = true; } TokenKind::KwPrivate => { export.private = true; } TokenKind::EqualEqual => { let token = self.read(); if let Some(value) = token.value { export.alias_target = value.to_string(); } else { return Err(Error::new( ErrorKind::InvalidInput, format!("expected identifier, found: {:?}", token.kind), )); } // Skipped mingw i386 handling // See https://github.com/llvm/llvm-project/blob/09c2b7c35af8c4bad39f03e9f60df8bd07323028/llvm/lib/Object/COFFModuleDefinition.cpp#L282-L283 } _ => { self.stack.push(token); self.def.exports.push(export); break; } } } Ok(()) } // HEAPSIZE/STACKSIZE reserve[,commit] fn parse_numbers(&mut self) -> Result<(u64, u64)> { let reserve = self.read_as_int()?; let token = self.read(); if token.kind != TokenKind::Comma { self.stack.push(token); return Ok((reserve, 0)); } let commit = self.read_as_int()?; Ok((reserve, commit)) } // NAME outputPath [BASE=address] fn parse_name(&mut self) -> Result<(String, u64)> { let mut name = String::new(); let token = self.read(); if token.kind == TokenKind::Identifier { name = token.unwrap_value().to_string(); } else { self.stack.push(token); return Ok((name, 0)); } let token = self.read(); return if token.kind == TokenKind::KwBase { let token = self.read(); if token.kind != TokenKind::Equal { return Err(Error::new( ErrorKind::InvalidInput, format!("expected equal, found: {:?}", token.kind), )); } let base = self.read_as_int()?; Ok((name, base)) } else { self.stack.push(token); Ok((name, 0)) }; } // VERSION major[.minor] fn parse_version(&mut self) -> Result<(u32, u32)> { let token = self.read(); if token.kind != TokenKind::Identifier { return Err(Error::new( ErrorKind::InvalidInput, format!("expected identifier, found: {:?}", token.kind), )); } let value = token.unwrap_value(); match value.split_once('.') { Some((major, minor)) => { let major = major .parse::() .map_err(|_| Error::new(ErrorKind::InvalidInput, "expected integer"))?; let minor = minor .parse::() .map_err(|_| Error::new(ErrorKind::InvalidInput, "expected integer"))?; Ok((major, minor)) } None => { let major = value .parse::() .map_err(|_| Error::new(ErrorKind::InvalidInput, "expected integer"))?; Ok((major, 0)) } } } fn read(&mut self) -> Token<'a> { if let Some(token) = self.stack.pop() { token } else { self.lexer.next().expect("unexpected EOF") } } fn read_as_int(&mut self) -> Result { let token = self.read(); if token.kind != TokenKind::Identifier { return Err(Error::new( ErrorKind::InvalidInput, format!("expected identifier, found: {:?}", token.kind), )); } token .unwrap_value() .parse() .map_err(|_| Error::new(ErrorKind::InvalidInput, "expected integer")) } } fn is_decorated(sym: &str) -> bool { sym.starts_with('@') || sym.starts_with('?') || sym.contains('@') } #[cfg(test)] mod test { use super::*; #[test] fn test_lexer() { let mut lexer = Lexer::new(r#"NAME foo.dll"#); assert_eq!( lexer.next(), Some(Token { kind: TokenKind::KwName, value: Some("NAME"), }) ); assert_eq!( lexer.next(), Some(Token { kind: TokenKind::Identifier, value: Some("foo.dll"), }) ); assert_eq!( lexer.next(), Some(Token { kind: TokenKind::Eof, value: None, }) ); let mut lexer = Lexer::new(""); assert_eq!( lexer.next(), Some(Token { kind: TokenKind::Eof, value: None, }) ); let mut lexer = Lexer::new("\0"); assert_eq!( lexer.next(), Some(Token { kind: TokenKind::Eof, value: None, }) ); let mut lexer = Lexer::new(r#"=,=="CODE"BASE;"#); assert_eq!( lexer.next(), Some(Token { kind: TokenKind::Equal, value: Some("="), }) ); assert_eq!( lexer.next(), Some(Token { kind: TokenKind::Comma, value: Some(","), }) ); assert_eq!( lexer.next(), Some(Token { kind: TokenKind::EqualEqual, value: Some("=="), }) ); assert_eq!( lexer.next(), Some(Token { kind: TokenKind::Identifier, value: Some("CODE"), }) ); assert_eq!( lexer.next(), Some(Token { kind: TokenKind::KwBase, value: Some("BASE"), }) ); assert_eq!( lexer.next(), Some(Token { kind: TokenKind::Eof, value: None, }) ); } #[test] fn test_parser() { Parser::new("", MachineType::AMD64).parse().unwrap(); let def = Parser::new("NAME foo", MachineType::AMD64).parse().unwrap(); assert_eq!(def.import_name, "foo"); let def = Parser::new("LIBRARY foo.dll", MachineType::AMD64) .parse() .unwrap(); assert_eq!(def.import_name, "foo.dll"); let def = Parser::new(";\n; comment\nLIBRARY foo.dll", MachineType::AMD64) .parse() .unwrap(); assert_eq!(def.import_name, "foo.dll"); let def = Parser::new( r#"; ; Definition file of python310.dll ; Automatic generated by gendef ; written by Kai Tietz 2008 ; LIBRARY "python310.dll" EXPORTS PyAIter_Check PyArg_Parse PyByteArray_Type DATA PyBytesIter_Type DATA"#, MachineType::AMD64, ) .parse() .unwrap(); assert_eq!(def.import_name, "python310.dll"); assert_eq!(def.exports.len(), 4); assert_eq!(def.exports[0].name, "PyAIter_Check"); assert!(!def.exports[0].data); assert_eq!(def.exports[1].name, "PyArg_Parse"); assert!(!def.exports[1].data); assert_eq!(def.exports[2].name, "PyByteArray_Type"); assert!(def.exports[2].data); assert_eq!(def.exports[3].name, "PyBytesIter_Type"); assert!(def.exports[3].data); } #[test] fn test_parser_with_bad_input() { Parser::new(" \u{b}EXPORTS D \u{b}===", MachineType::AMD64) .parse() .unwrap_err(); Parser::new("EXPORTS 8= @", MachineType::AMD64) .parse() .unwrap_err(); } } implib-0.3.3/src/gnu.rs000064400000000000000000000453101046102023000130340ustar 00000000000000use std::io::{Error, ErrorKind, Seek, Write}; use object::pe::*; use object::write::{Object, Relocation, Symbol, SymbolId, SymbolSection}; use object::{ BinaryFormat, Endianness, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, }; use crate::def::{ModuleDef, ShortExport}; use crate::{ar, ArchiveMember, MachineType}; const JMP_IX86_BYTES: [u8; 8] = [0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90]; const JMP_ARM_BYTES: [u8; 12] = [ 0x00, 0xc0, 0x9f, 0xe5, /* ldr ip, [pc] */ 0x00, 0xf0, 0x9c, 0xe5, /* ldr pc, [ip] */ 0, 0, 0, 0, ]; impl MachineType { fn to_arch(self) -> object::Architecture { use object::Architecture::*; match self { Self::AMD64 => X86_64, Self::ARMNT => Arm, Self::ARM64 => Aarch64, Self::I386 => I386, } } } /// GNU flavored Windows import library generator #[derive(Debug, Clone)] pub struct GnuImportLibrary { def: ModuleDef, machine: MachineType, } impl GnuImportLibrary { /// Create new import library generator from `ModuleDef` pub fn new(mut def: ModuleDef, machine: MachineType) -> Self { // If ext_name is set (if the "ext_name = name" syntax was used), overwrite // name with ext_name and clear ext_name. When only creating an import // library and not linking, the internal name is irrelevant. for export in &mut def.exports { if let Some(ext_name) = export.ext_name.take() { export.name = ext_name; } } // Skipped i386 handling // See https://github.com/llvm/llvm-project/blob/09c2b7c35af8c4bad39f03e9f60df8bd07323028/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L197-L212 GnuImportLibrary { def, machine } } /// Write out the import library pub fn write_to(&self, writer: &mut W) -> Result<(), Error> { let mut members = Vec::new(); let mut factory = ObjectFactory::new(&self.def.import_name, self.machine)?; for export in &self.def.exports { members.push(factory.make_one(export)?.create_archive_entry()); } members.push(factory.make_head()?.create_archive_entry()); members.push(factory.make_tail()?.create_archive_entry()); members.reverse(); let identifiers = members .iter() .map(|(header, _)| header.identifier().to_vec()) .collect(); let symbol_table: Vec>> = members .iter() .map(|(_, member)| { member .symbols .iter() .map(|s| s.to_string().into_bytes()) .collect::>>() }) .collect(); let mut archive = ar::GnuBuilder::new_with_symbol_table(writer, true, identifiers, symbol_table)?; for (header, member) in members { archive.append(&header, &member.data[..])?; } Ok(()) } } #[derive(Debug)] struct ObjectFactory<'a> { machine: MachineType, import_name: &'a str, output_name: String, seq: usize, } impl<'a> ObjectFactory<'a> { fn new(import_name: &'a str, machine: MachineType) -> Result { if import_name.contains('\0') { return Err(Error::new( ErrorKind::InvalidInput, "import name contains null byte".to_string(), )); } Ok(Self { machine, import_name, output_name: format!("{}.a", import_name), seq: 0, }) } fn make_relocation( &self, offset: u64, symbol: SymbolId, addend: i64, rel_kind: u16, ) -> Relocation { Relocation { offset, symbol, addend, flags: object::RelocationFlags::Coff { typ: rel_kind }, } } fn make_head(&self) -> Result { let mut obj = Object::new( BinaryFormat::Coff, self.machine.to_arch(), Endianness::Little, ); let text_sec = obj.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); obj.section_mut(text_sec).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ, }; let data_sec = obj.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data); obj.section_mut(data_sec).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let bss_sec = obj.add_section(Vec::new(), b".bss".to_vec(), SectionKind::UninitializedData); obj.section_mut(bss_sec).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let id2 = obj.add_section(Vec::new(), b".idata$2".to_vec(), SectionKind::Data); let id5 = obj.add_section(Vec::new(), b".idata$5".to_vec(), SectionKind::Data); obj.section_mut(id5).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let id4 = obj.add_section(Vec::new(), b".idata$4".to_vec(), SectionKind::Data); obj.section_mut(id4).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; obj.add_file_symbol(b"fake".to_vec()); let id5_sym = obj.section_symbol(id5); let id4_sym = obj.section_symbol(id4); let img_rel = self.machine.img_rel_relocation(); obj.add_relocation(id2, self.make_relocation(0, id4_sym, 0, img_rel)) .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; obj.add_relocation(id2, self.make_relocation(16, id5_sym, 0, img_rel)) .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; let import_name = self.import_name.replace('.', "_"); let head_sym_name = format!("_head_{}", import_name); let head_sym = Symbol { name: head_sym_name.as_bytes().to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Dynamic, weak: false, section: SymbolSection::Section(id2), flags: SymbolFlags::None, }; obj.add_symbol(head_sym); let iname_sym = Symbol { name: format!("{}_iname", import_name).into_bytes(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Dynamic, weak: false, section: SymbolSection::Undefined, flags: SymbolFlags::None, }; let iname_sym_id = obj.add_symbol(iname_sym); obj.append_section_data(id2, &[0; 20], 4); obj.add_relocation(id2, self.make_relocation(12, iname_sym_id, 0, img_rel)) .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; Ok(ArchiveMember { name: format!("{}_h.o", self.output_name.replace('.', "_")), data: obj .write() .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?, symbols: vec![head_sym_name], }) } fn make_tail(&self) -> Result { let mut obj = Object::new( BinaryFormat::Coff, self.machine.to_arch(), Endianness::Little, ); let text_sec = obj.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); obj.section_mut(text_sec).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ, }; let data_sec = obj.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data); obj.section_mut(data_sec).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let bss_sec = obj.add_section(Vec::new(), b".bss".to_vec(), SectionKind::UninitializedData); obj.section_mut(bss_sec).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let id4 = obj.add_section(Vec::new(), b".idata$4".to_vec(), SectionKind::Data); obj.section_mut(id4).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let id5 = obj.add_section(Vec::new(), b".idata$5".to_vec(), SectionKind::Data); obj.section_mut(id5).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let id7 = obj.add_section(Vec::new(), b".idata$7".to_vec(), SectionKind::Data); obj.section_mut(id4).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; obj.add_file_symbol(b"fake".to_vec()); let import_name = self.import_name.replace('.', "_"); let iname_sym_name = format!("{}_iname", import_name); let iname_sym = Symbol { name: iname_sym_name.as_bytes().to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Dynamic, weak: false, section: SymbolSection::Section(id7), flags: SymbolFlags::None, }; obj.add_symbol(iname_sym); obj.append_section_data(id4, &[0; 8], 4); obj.append_section_data(id5, &[0; 8], 4); let mut import_name_bytes = self.import_name.as_bytes().to_vec(); import_name_bytes.push(b'\0'); obj.append_section_data(id7, &import_name_bytes, 4); Ok(ArchiveMember { name: format!("{}_t.o", self.output_name.replace('.', "_")), data: obj .write() .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?, symbols: vec![iname_sym_name], }) } fn make_one(&mut self, export: &ShortExport) -> Result { if export.name.contains('\0') { return Err(Error::new( ErrorKind::InvalidInput, "export name contains null byte".to_string(), )); } let mut obj = Object::new( BinaryFormat::Coff, self.machine.to_arch(), Endianness::Little, ); let text_sec = obj.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); obj.section_mut(text_sec).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ, }; let data_sec = obj.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data); obj.section_mut(data_sec).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let bss_sec = obj.add_section(Vec::new(), b".bss".to_vec(), SectionKind::UninitializedData); obj.section_mut(bss_sec).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let id7 = obj.add_section(Vec::new(), b".idata$7".to_vec(), SectionKind::Data); obj.section_mut(id7).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let id5 = obj.add_section(Vec::new(), b".idata$5".to_vec(), SectionKind::Data); obj.section_mut(id5).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let id4 = obj.add_section(Vec::new(), b".idata$4".to_vec(), SectionKind::Data); obj.section_mut(id4).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let id6 = obj.add_section(Vec::new(), b".idata$6".to_vec(), SectionKind::Data); obj.section_mut(id6).flags = SectionFlags::Coff { characteristics: IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, }; let import_name = self.import_name.replace('.', "_"); let head_sym = Symbol { name: format!("_head_{}", import_name).into_bytes(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Dynamic, weak: false, section: SymbolSection::Undefined, flags: SymbolFlags::None, }; let head_sym = obj.add_symbol(head_sym); let mut archive_symbols = Vec::new(); if !export.data { let exp_sym = Symbol { name: export.name.as_bytes().to_vec(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Dynamic, weak: false, section: SymbolSection::Section(text_sec), flags: SymbolFlags::None, }; obj.add_symbol(exp_sym); archive_symbols.push(export.name.to_string()); } let exp_imp_sym = Symbol { name: format!("__imp_{}", export.name).into_bytes(), value: 0, size: 0, kind: SymbolKind::Data, scope: SymbolScope::Dynamic, weak: false, section: SymbolSection::Section(id5), flags: SymbolFlags::None, }; let exp_imp_sym = obj.add_symbol(exp_imp_sym); archive_symbols.push(format!("__imp_{}", export.name)); if !export.data { let (jmp_stub, offset, rel_kind) = match self.machine { MachineType::I386 => (&JMP_IX86_BYTES[..], 2, IMAGE_REL_I386_REL32), MachineType::ARMNT => (&JMP_ARM_BYTES[..], 8, IMAGE_REL_ARM_REL32), MachineType::AMD64 => (&JMP_IX86_BYTES[..], 2, IMAGE_REL_AMD64_REL32), MachineType::ARM64 => (&JMP_ARM_BYTES[..], 8, IMAGE_REL_ARM64_REL32), }; obj.append_section_data(text_sec, jmp_stub, 4); obj.add_relocation( text_sec, self.make_relocation(offset, exp_imp_sym, 0, rel_kind), ) .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; } let img_rel = self.machine.img_rel_relocation(); obj.append_section_data(id7, &[0; 4], 4); obj.add_relocation(id7, self.make_relocation(0, head_sym, 0, img_rel)) .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; let id6_sym = obj.section_symbol(id6); let id5_data = if export.no_name { [ export.ordinal as u8, (export.ordinal >> 8) as u8, 0, 0, 0, 0, 0, 0x80, ] } else { obj.add_relocation(id5, self.make_relocation(0, id6_sym, 0, img_rel)) .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; [0; 8] }; obj.append_section_data(id5, &id5_data, 4); let id4_data = if export.no_name { [ export.ordinal as u8, (export.ordinal >> 8) as u8, 0, 0, 0, 0, 0, 0x80, ] } else { obj.add_relocation(id4, self.make_relocation(0, id6_sym, 0, img_rel)) .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; [0; 8] }; obj.append_section_data(id4, &id4_data, 4); if !export.no_name { let len = 2 + export.name.len() + 1; let mut id6_data = vec![0; len]; let ord = export.ordinal; id6_data[0] = ord as u8; id6_data[1] = (ord >> 8) as u8; id6_data[2..len - 1].copy_from_slice(export.name.as_bytes()); obj.append_section_data(id6, &id6_data, 2); } let name = format!("{}_s{:05}.o", self.output_name.replace('.', "_"), self.seq); self.seq += 1; Ok(ArchiveMember { name, data: obj .write() .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?, symbols: archive_symbols, }) } } #[cfg(test)] mod test { use super::*; use std::io::Cursor; #[test] fn test_gnu_with_bad_input() { let import_lib = GnuImportLibrary::new( ModuleDef::parse("EXPORTS D\u{c}\0", MachineType::AMD64).unwrap(), MachineType::AMD64, ); import_lib .write_to(&mut Cursor::new(Vec::new())) .unwrap_err(); } #[ignore] #[test] fn debug_head_tail_export() { let mut factory = ObjectFactory::new("python39.dll", MachineType::AMD64).unwrap(); let head = factory.make_head().unwrap(); std::fs::write("head.o", head.data).unwrap(); let tail = factory.make_tail().unwrap(); std::fs::write("tail.o", tail.data).unwrap(); let export = ShortExport { name: "PyAST_CompileEx".to_string(), ext_name: None, symbol_name: "".to_string(), alias_target: "".to_string(), ordinal: 0, no_name: false, data: false, private: false, constant: false, }; let exp = factory.make_one(&export).unwrap(); std::fs::write("exp.o", exp.data).unwrap(); } } implib-0.3.3/src/lib.rs000064400000000000000000000076741046102023000130240ustar 00000000000000#[cfg(any(not(feature = "msvc"), not(feature = "gnu")))] use std::io::ErrorKind; use std::io::{Error, Seek, Write}; use object::pe::*; /// Unix archiver writer #[cfg(any(feature = "msvc", feature = "gnu"))] mod ar; /// Parse .DEF file pub mod def; /// GNU binutils flavored import library #[cfg(feature = "gnu")] mod gnu; /// MSVC flavored import library #[cfg(feature = "msvc")] mod msvc; #[cfg(feature = "gnu")] use self::gnu::GnuImportLibrary; #[cfg(feature = "msvc")] use self::msvc::MsvcImportLibrary; use crate::def::ModuleDef; /// Machine types #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u16)] pub enum MachineType { /// Intel 386 I386 = IMAGE_FILE_MACHINE_I386, /// ARM Thumb-2 Little-Endian ARMNT = IMAGE_FILE_MACHINE_ARMNT, /// AMD64 (K8) AMD64 = IMAGE_FILE_MACHINE_AMD64, ARM64 = IMAGE_FILE_MACHINE_ARM64, } impl MachineType { fn img_rel_relocation(&self) -> u16 { match self { Self::AMD64 => IMAGE_REL_AMD64_ADDR32NB, Self::ARMNT => IMAGE_REL_ARM_ADDR32NB, Self::ARM64 => IMAGE_REL_ARM64_ADDR32NB, Self::I386 => IMAGE_REL_I386_DIR32NB, } } } #[derive(Debug)] struct ArchiveMember { name: String, data: Vec, symbols: Vec, } impl ArchiveMember { #[cfg(any(feature = "msvc", feature = "gnu"))] fn create_archive_entry(self) -> (ar::Header, ArchiveMember) { let mut header = ar::Header::new(self.name.to_string().into_bytes(), self.data.len() as u64); header.set_mode(0o644); (header, self) } } /// Import library flavor #[derive(Debug, Clone, Copy)] pub enum Flavor { /// MSVC short import library Msvc, /// GNU(MinGW) import library Gnu, } /// Windows import library generator #[derive(Debug, Clone)] pub struct ImportLibrary { def: ModuleDef, machine: MachineType, flavor: Flavor, } impl ImportLibrary { /// Create new import library generator from module definition text content pub fn new(def: &str, machine: MachineType, flavor: Flavor) -> Result { let def = ModuleDef::parse(def, machine)?; Ok(Self::from_def(def, machine, flavor)) } /// Create new import library generator from `ModuleDef` pub fn from_def(mut def: ModuleDef, machine: MachineType, flavor: Flavor) -> Self { // If ext_name is set (if the "ext_name = name" syntax was used), overwrite // name with ext_name and clear ext_name. When only creating an import // library and not linking, the internal name is irrelevant. for export in &mut def.exports { if let Some(ext_name) = export.ext_name.take() { export.name = ext_name; } } // Skipped i386 handling // See https://github.com/llvm/llvm-project/blob/09c2b7c35af8c4bad39f03e9f60df8bd07323028/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L197-L212 ImportLibrary { def, machine, flavor, } } /// Get import library name pub fn import_name(&self) -> &str { &self.def.import_name } /// Write out the import library pub fn write_to(self, writer: &mut W) -> Result<(), Error> { match self.flavor { #[cfg(feature = "msvc")] Flavor::Msvc => MsvcImportLibrary::new(self.def, self.machine).write_to(writer), #[cfg(not(feature = "msvc"))] Flavor::Msvc => Err(Error::new( ErrorKind::Unsupported, "MSVC import library unsupported, enable 'msvc' feature to use it", )), #[cfg(feature = "gnu")] Flavor::Gnu => GnuImportLibrary::new(self.def, self.machine).write_to(writer), #[cfg(not(feature = "gnu"))] Flavor::Gnu => Err(Error::new( ErrorKind::Unsupported, "GNU import library unsupported, enable 'gnu' feature to use it", )), } } } implib-0.3.3/src/msvc.rs000064400000000000000000000771201046102023000132170ustar 00000000000000use std::io::{Error, Seek, Write}; use std::mem::size_of; use memoffset::offset_of; use object::endian::{LittleEndian as LE, U16Bytes, U32Bytes, U16, U32}; use object::pe::*; use object::pod::bytes_of; use crate::def::{ModuleDef, ShortExport}; use crate::{ar, ArchiveMember, MachineType}; const NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME: &str = "__NULL_IMPORT_DESCRIPTOR"; #[derive(Debug, Clone, Copy)] #[repr(u16)] enum ImportType { /// Code, function Code, /// Data Data, /// Constant Const, } impl ShortExport { fn import_type(&self) -> ImportType { if self.data { ImportType::Data } else if self.constant { ImportType::Const } else { ImportType::Code } } } #[derive(Debug, Clone, Copy)] #[repr(u16)] enum ImportNameType { /// Import is by ordinal. This indicates that the value in the Ordinal/Hint /// field of the import header is the import's ordinal. If this constant is /// not specified, then the Ordinal/Hint field should always be interpreted /// as the import's hint. Ordinal = IMPORT_OBJECT_ORDINAL, /// The import name is identical to the public symbol name Name = IMPORT_OBJECT_NAME, /// The import name is the public symbol name, but skipping the leading ?, /// @, or optionally _. NameNoPrefix = IMPORT_OBJECT_NAME_NO_PREFIX, /// The import name is the public symbol name, but skipping the leading ?, /// @, or optionally _, and truncating at the first @. NameUndecorate = IMPORT_OBJECT_NAME_UNDECORATE, } impl MachineType { fn is_32bit(&self) -> bool { matches!(self, Self::ARMNT | Self::I386) } } /// MSVC flavored Windows import library generator #[derive(Debug, Clone)] pub struct MsvcImportLibrary { def: ModuleDef, machine: MachineType, } impl MsvcImportLibrary { /// Create new import library generator from `ModuleDef` pub fn new(mut def: ModuleDef, machine: MachineType) -> Self { // If ext_name is set (if the "ext_name = name" syntax was used), overwrite // name with ext_name and clear ext_name. When only creating an import // library and not linking, the internal name is irrelevant. for export in &mut def.exports { if let Some(ext_name) = export.ext_name.take() { export.name = ext_name; } } // Skipped i386 handling // See https://github.com/llvm/llvm-project/blob/09c2b7c35af8c4bad39f03e9f60df8bd07323028/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L197-L212 MsvcImportLibrary { def, machine } } fn get_name_type(&self, sym: &str, ext_name: &str) -> ImportNameType { // Skipped mingw64 handling // See https://github.com/llvm/llvm-project/blob/09c2b7c35af8c4bad39f03e9f60df8bd07323028/llvm/lib/Object/COFFImportFile.cpp#L96-L105 if ext_name.starts_with('_') && ext_name.contains('@') { ImportNameType::Name } else if sym != ext_name { ImportNameType::NameUndecorate } else if self.machine == MachineType::I386 && sym.starts_with('_') { ImportNameType::NameNoPrefix } else { ImportNameType::Name } } /// Write out the import library pub fn write_to(&self, writer: &mut W) -> Result<(), Error> { let mut members = Vec::new(); let factory = ObjectFactory::new(&self.def.import_name, self.machine); let import_descriptor = factory.create_import_descriptor(); members.push(import_descriptor.create_archive_entry()); let null_import_descriptor = factory.create_null_import_descriptor(); members.push(null_import_descriptor.create_archive_entry()); let null_thunk = factory.create_null_thunk(); members.push(null_thunk.create_archive_entry()); for export in &self.def.exports { if export.private { continue; } let sym = if export.symbol_name.is_empty() { &export.name } else { &export.symbol_name }; let name_type = if export.no_name { ImportNameType::Ordinal } else { self.get_name_type(sym, &export.name) }; let name = if let Some(ext_name) = &export.ext_name { replace(sym, &export.name, ext_name)? } else { sym.to_string() }; if !export.alias_target.is_empty() && name != export.alias_target { let weak_non_imp = factory.create_weak_external(&export.alias_target, &name, false); members.push(weak_non_imp.create_archive_entry()); let weak_imp = factory.create_weak_external(&export.alias_target, &name, true); members.push(weak_imp.create_archive_entry()); } let short_import = factory.create_short_import(&name, export.ordinal, export.import_type(), name_type); members.push(short_import.create_archive_entry()); } let identifiers = members .iter() .map(|(header, _)| header.identifier().to_vec()) .collect(); let symbol_table: Vec>> = members .iter() .map(|(_, member)| { member .symbols .iter() .map(|s| s.to_string().into_bytes()) .collect::>>() }) .collect(); let mut archive = ar::GnuBuilder::new_with_symbol_table(writer, true, identifiers, symbol_table)?; for (header, member) in members { archive.append(&header, &member.data[..])?; } Ok(()) } } fn replace(sym: &str, from: &str, to: &str) -> Result { use std::io::ErrorKind; match sym.find(from) { Some(pos) => return Ok(format!("{}{}{}", &sym[..pos], to, &sym[pos + from.len()..])), None => { if from.starts_with('_') && to.starts_with('_') { if let Some(pos) = sym.find(&from[1..]) { return Ok(format!( "{}{}{}", &sym[..pos], &to[1..], &sym[pos + from.len() - 1..] )); } } } } Err(Error::new( ErrorKind::InvalidInput, format!("{}: replacing '{}' with '{}' failed", sym, from, to), )) } /// Constructs various small object files necessary to support linking /// symbols imported from a DLL. The contents are pretty strictly defined and /// nearly entirely static. The details of the structures files are defined in /// WINNT.h and the PE/COFF specification. #[derive(Debug)] struct ObjectFactory<'a> { machine: MachineType, import_name: &'a str, import_descriptor_symbol_name: String, null_thunk_symbol_name: String, } impl<'a> ObjectFactory<'a> { fn new(import_name: &'a str, machine: MachineType) -> Self { let library = if import_name.ends_with(".dll") || import_name.ends_with(".exe") { &import_name[..import_name.len() - 4] } else { import_name }; Self { machine, import_name, import_descriptor_symbol_name: format!("__IMPORT_DESCRIPTOR_{}", library), null_thunk_symbol_name: format!("\x7f{}_NULL_THUNK_DATA", library), } } fn write_string_table(buffer: &mut Vec, strings: &[&str]) { // The COFF string table consists of a 4-byte value which is the size of the // table, including the length field itself. This value is followed by the // string content itself, which is an array of null-terminated C-style // strings. The termination is important as they are referenced to by offset // by the symbol entity in the file format. let offset = buffer.len(); // Skip over the length field, we will fill it in later as we will have // computed the length while emitting the string content itself. buffer.extend_from_slice(&[0, 0, 0, 0]); for s in strings { buffer.extend(s.as_bytes()); buffer.push(b'\0'); } // Backfill the length of the table now that it has been computed. let size = (buffer.len() - offset) as u32; buffer[offset..offset + 4].copy_from_slice(&size.to_le_bytes()); } /// Creates an Import Descriptor. This is a small object file which contains a /// reference to the terminators and contains the library name (entry) for the /// import name table. It will force the linker to construct the necessary /// structure to import symbols from the DLL. fn create_import_descriptor(&self) -> ArchiveMember { const NUM_SECTIONS: usize = 2; const NUM_SYMBOLS: usize = 7; const NUM_RELOCATIONS: usize = 3; let mut buffer = Vec::new(); let pointer_to_symbol_table = size_of::() + NUM_SECTIONS * size_of::() // .idata$2 + size_of::() + NUM_RELOCATIONS * size_of::() // .idata$4 + self.import_name.len() + 1; let characteristics = if self.machine.is_32bit() { IMAGE_FILE_32BIT_MACHINE } else { 0 }; let header = ImageFileHeader { machine: U16::new(LE, self.machine as u16), number_of_sections: U16::new(LE, NUM_SECTIONS as u16), time_date_stamp: U32::new(LE, 0), pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32), number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32), size_of_optional_header: U16::new(LE, 0), characteristics: U16::new(LE, characteristics), }; buffer.extend_from_slice(bytes_of(&header)); // Section Header Table let section_header_table = [ ImageSectionHeader { name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'2'], virtual_size: U32::new(LE, 0), virtual_address: U32::new(LE, 0), size_of_raw_data: U32::new(LE, size_of::() as _), pointer_to_raw_data: U32::new( LE, (size_of::() + NUM_SECTIONS * size_of::()) as _, ), pointer_to_relocations: U32::new( LE, (size_of::() + NUM_SECTIONS * size_of::() + size_of::()) as _, ), pointer_to_linenumbers: U32::new(LE, 0), number_of_relocations: U16::new(LE, NUM_RELOCATIONS as _), number_of_linenumbers: U16::new(LE, 0), characteristics: U32::new( LE, IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, ), }, ImageSectionHeader { name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'6'], virtual_size: U32::new(LE, 0), virtual_address: U32::new(LE, 0), size_of_raw_data: U32::new(LE, (self.import_name.len() + 1) as _), pointer_to_raw_data: U32::new( LE, (size_of::() + NUM_SECTIONS * size_of::() + size_of::() + NUM_RELOCATIONS * size_of::()) as _, ), pointer_to_relocations: U32::new(LE, 0), pointer_to_linenumbers: U32::new(LE, 0), number_of_relocations: U16::new(LE, 0), number_of_linenumbers: U16::new(LE, 0), characteristics: U32::new( LE, IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, ), }, ]; for section in section_header_table { buffer.extend_from_slice(bytes_of(§ion)); } // .idata$2 let import_descriptor = ImageImportDescriptor { original_first_thunk: U32Bytes::new(LE, 0), time_date_stamp: U32Bytes::new(LE, 0), forwarder_chain: U32Bytes::new(LE, 0), name: U32Bytes::new(LE, 0), first_thunk: U32Bytes::new(LE, 0), }; buffer.extend_from_slice(bytes_of(&import_descriptor)); let relocation_table = [ ImageRelocation { virtual_address: U32Bytes::new(LE, offset_of!(ImageImportDescriptor, name) as _), symbol_table_index: U32Bytes::new(LE, 2), typ: U16Bytes::new(LE, self.machine.img_rel_relocation()), }, ImageRelocation { virtual_address: U32Bytes::new( LE, offset_of!(ImageImportDescriptor, original_first_thunk) as _, ), symbol_table_index: U32Bytes::new(LE, 3), typ: U16Bytes::new(LE, self.machine.img_rel_relocation()), }, ImageRelocation { virtual_address: U32Bytes::new( LE, offset_of!(ImageImportDescriptor, first_thunk) as _, ), symbol_table_index: U32Bytes::new(LE, 4), typ: U16Bytes::new(LE, self.machine.img_rel_relocation()), }, ]; for relocation in &relocation_table { buffer.extend_from_slice(bytes_of(relocation)); } // .idata$6 buffer.extend_from_slice(self.import_name.as_bytes()); buffer.push(b'\0'); // Symbol Table let sym5_offset = (size_of::() + self.import_descriptor_symbol_name.len() + 1).to_le_bytes(); let sym6_offset = (size_of::() + self.import_descriptor_symbol_name.len() + 1 + NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME.len() + 1) .to_le_bytes(); let symbol_table = [ ImageSymbol { name: [0, 0, 0, 0, size_of::() as _, 0, 0, 0], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 1), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_EXTERNAL, number_of_aux_symbols: 0, }, ImageSymbol { name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'2'], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 1), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_SECTION, number_of_aux_symbols: 0, }, ImageSymbol { name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'6'], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 2), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_STATIC, number_of_aux_symbols: 0, }, ImageSymbol { name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'4'], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 0), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_SECTION, number_of_aux_symbols: 0, }, ImageSymbol { name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'5'], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 0), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_SECTION, number_of_aux_symbols: 0, }, ImageSymbol { name: [ 0, 0, 0, 0, sym5_offset[0], sym5_offset[1], sym5_offset[2], sym5_offset[3], ], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 0), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_EXTERNAL, number_of_aux_symbols: 0, }, ImageSymbol { name: [ 0, 0, 0, 0, sym6_offset[0], sym6_offset[1], sym6_offset[2], sym6_offset[3], ], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 0), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_EXTERNAL, number_of_aux_symbols: 0, }, ]; for table in &symbol_table { buffer.extend_from_slice(bytes_of(table)); } Self::write_string_table( &mut buffer, &[ &self.import_descriptor_symbol_name, NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME, &self.null_thunk_symbol_name, ], ); ArchiveMember { name: self.import_name.to_string(), data: buffer, symbols: vec![self.import_descriptor_symbol_name.to_string()], } } /// Creates a NULL import descriptor. This is a small object file whcih /// contains a NULL import descriptor. It is used to terminate the imports /// from a specific DLL. fn create_null_import_descriptor(&self) -> ArchiveMember { const NUM_SECTIONS: usize = 1; const NUM_SYMBOLS: usize = 1; let mut buffer = Vec::new(); let pointer_to_symbol_table = size_of::() + NUM_SECTIONS * size_of::() // .idata$3 + size_of::(); let characteristics = if self.machine.is_32bit() { IMAGE_FILE_32BIT_MACHINE } else { 0 }; let header = ImageFileHeader { machine: U16::new(LE, self.machine as u16), number_of_sections: U16::new(LE, NUM_SECTIONS as u16), time_date_stamp: U32::new(LE, 0), pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32), number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32), size_of_optional_header: U16::new(LE, 0), characteristics: U16::new(LE, characteristics), }; buffer.extend_from_slice(bytes_of(&header)); // Section Header Table let section_header_table = ImageSectionHeader { name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'3'], virtual_size: U32::new(LE, 0), virtual_address: U32::new(LE, 0), size_of_raw_data: U32::new(LE, size_of::() as _), pointer_to_raw_data: U32::new( LE, (size_of::() + NUM_SECTIONS * size_of::()) as _, ), pointer_to_relocations: U32::new(LE, 0), pointer_to_linenumbers: U32::new(LE, 0), number_of_relocations: U16::new(LE, 0), number_of_linenumbers: U16::new(LE, 0), characteristics: U32::new( LE, IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, ), }; buffer.extend_from_slice(bytes_of(§ion_header_table)); // .idata$3 let import_descriptor = ImageImportDescriptor { original_first_thunk: U32Bytes::new(LE, 0), time_date_stamp: U32Bytes::new(LE, 0), forwarder_chain: U32Bytes::new(LE, 0), name: U32Bytes::new(LE, 0), first_thunk: U32Bytes::new(LE, 0), }; buffer.extend_from_slice(bytes_of(&import_descriptor)); // Symbol Table let symbol_table = ImageSymbol { name: [0, 0, 0, 0, size_of::() as _, 0, 0, 0], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 1), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_EXTERNAL, number_of_aux_symbols: 0, }; buffer.extend_from_slice(bytes_of(&symbol_table)); Self::write_string_table(&mut buffer, &[NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME]); ArchiveMember { name: self.import_name.to_string(), data: buffer, symbols: vec![NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME.to_string()], } } /// Create a NULL Thunk Entry. This is a small object file which contains a /// NULL Import Address Table entry and a NULL Import Lookup Table Entry. It /// is used to terminate the IAT and ILT. fn create_null_thunk(&self) -> ArchiveMember { const NUM_SECTIONS: usize = 2; const NUM_SYMBOLS: usize = 1; let mut buffer = Vec::new(); let va_size = if self.machine.is_32bit() { 4 } else { 8 }; let pointer_to_symbol_table = size_of::() + NUM_SECTIONS * size_of::() // .idata$5 + va_size // .idata$4 + va_size; let characteristics = if self.machine.is_32bit() { IMAGE_FILE_32BIT_MACHINE } else { 0 }; let header = ImageFileHeader { machine: U16::new(LE, self.machine as u16), number_of_sections: U16::new(LE, NUM_SECTIONS as u16), time_date_stamp: U32::new(LE, 0), pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32), number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32), size_of_optional_header: U16::new(LE, 0), characteristics: U16::new(LE, characteristics), }; buffer.extend_from_slice(bytes_of(&header)); // Section Header Table let align = if self.machine.is_32bit() { IMAGE_SCN_ALIGN_4BYTES } else { IMAGE_SCN_ALIGN_8BYTES }; let section_header_table = [ ImageSectionHeader { name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'5'], virtual_size: U32::new(LE, 0), virtual_address: U32::new(LE, 0), size_of_raw_data: U32::new(LE, va_size as _), pointer_to_raw_data: U32::new( LE, (size_of::() + NUM_SECTIONS * size_of::()) as _, ), pointer_to_relocations: U32::new(LE, 0), pointer_to_linenumbers: U32::new(LE, 0), number_of_relocations: U16::new(LE, 0), number_of_linenumbers: U16::new(LE, 0), characteristics: U32::new( LE, align | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, ), }, ImageSectionHeader { name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'4'], virtual_size: U32::new(LE, 0), virtual_address: U32::new(LE, 0), size_of_raw_data: U32::new(LE, va_size as _), pointer_to_raw_data: U32::new( LE, (size_of::() + NUM_SECTIONS * size_of::() + va_size) as _, ), pointer_to_relocations: U32::new(LE, 0), pointer_to_linenumbers: U32::new(LE, 0), number_of_relocations: U16::new(LE, 0), number_of_linenumbers: U16::new(LE, 0), characteristics: U32::new( LE, align | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, ), }, ]; for section in section_header_table { buffer.extend_from_slice(bytes_of(§ion)); } // .idata$5, ILT buffer.extend(0u32.to_le_bytes()); if !self.machine.is_32bit() { buffer.extend(0u32.to_le_bytes()); } // .idata$4, IAT buffer.extend(0u32.to_le_bytes()); if !self.machine.is_32bit() { buffer.extend(0u32.to_le_bytes()); } // Symbol Table let symbol_table = ImageSymbol { name: [0, 0, 0, 0, size_of::() as _, 0, 0, 0], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 1), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_EXTERNAL, number_of_aux_symbols: 0, }; buffer.extend_from_slice(bytes_of(&symbol_table)); Self::write_string_table(&mut buffer, &[&self.null_thunk_symbol_name]); ArchiveMember { name: self.import_name.to_string(), data: buffer, symbols: vec![self.null_thunk_symbol_name.to_string()], } } /// Create a short import file which is described in PE/COFF spec 7. Import /// Library Format. fn create_short_import( &self, sym: &str, ordinal: u16, import_type: ImportType, name_type: ImportNameType, ) -> ArchiveMember { // +2 for NULs let import_name_size = self.import_name.len() + sym.len() + 2; let size = size_of::() + import_name_size; let mut buffer = Vec::with_capacity(size); // Write short import header let import_header = ImportObjectHeader { sig1: U16::new(LE, 0), sig2: U16::new(LE, 0xFFFF), version: U16::new(LE, 0), machine: U16::new(LE, self.machine as _), time_date_stamp: U32::new(LE, 0), size_of_data: U32::new(LE, import_name_size as _), ordinal_or_hint: if ordinal > 0 { U16::new(LE, ordinal) } else { U16::new(LE, 0) }, name_type: U16::new(LE, ((name_type as u16) << 2) | import_type as u16), }; buffer.extend_from_slice(bytes_of(&import_header)); let symbols = if matches!(import_type, ImportType::Data) { vec![format!("__imp_{}", sym)] } else { vec![format!("__imp_{}", sym), sym.to_string()] }; // Write symbol name and DLL name buffer.extend(sym.as_bytes()); buffer.push(b'\0'); buffer.extend(self.import_name.as_bytes()); buffer.push(b'\0'); ArchiveMember { name: self.import_name.to_string(), data: buffer, symbols, } } /// Create a weak external file which is described in PE/COFF Aux Format 3. fn create_weak_external(&self, sym: &str, weak: &str, imp: bool) -> ArchiveMember { const NUM_SECTIONS: usize = 1; const NUM_SYMBOLS: usize = 5; let mut buffer = Vec::new(); let pointer_to_symbol_table = size_of::() + NUM_SECTIONS * size_of::(); let header = ImageFileHeader { machine: U16::new(LE, self.machine as u16), number_of_sections: U16::new(LE, NUM_SECTIONS as u16), time_date_stamp: U32::new(LE, 0), pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32), number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32), size_of_optional_header: U16::new(LE, 0), characteristics: U16::new(LE, 0), }; buffer.extend_from_slice(bytes_of(&header)); // Section Header Table let section_header_table = ImageSectionHeader { name: [b'.', b'd', b'r', b'e', b'c', b't', b'v', b'e'], virtual_size: U32::new(LE, 0), virtual_address: U32::new(LE, 0), size_of_raw_data: U32::new(LE, 0), pointer_to_raw_data: U32::new(LE, 0), pointer_to_relocations: U32::new(LE, 0), pointer_to_linenumbers: U32::new(LE, 0), number_of_relocations: U16::new(LE, 0), number_of_linenumbers: U16::new(LE, 0), characteristics: U32::new(LE, IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE), }; buffer.extend_from_slice(bytes_of(§ion_header_table)); // Symbol Table let prefix = if imp { "__imp_" } else { "" }; let sym3_offset = (size_of::() + sym.len() + prefix.len() + 1).to_le_bytes(); let symbol_table = [ ImageSymbol { name: [b'@', b'c', b'o', b'm', b'p', b'.', b'i', b'd'], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 0xFFFF), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_STATIC, number_of_aux_symbols: 0, }, ImageSymbol { name: [b'@', b'f', b'e', b'a', b't', b'.', b'0', b'0'], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 0xFFFF), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_STATIC, number_of_aux_symbols: 0, }, ImageSymbol { name: [0, 0, 0, 0, size_of::() as _, 0, 0, 0], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 0), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_EXTERNAL, number_of_aux_symbols: 0, }, ImageSymbol { name: [ 0, 0, 0, 0, sym3_offset[0], sym3_offset[1], sym3_offset[2], sym3_offset[3], ], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 0), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_WEAK_EXTERNAL, number_of_aux_symbols: 1, }, ImageSymbol { name: [2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS as u8, 0, 0, 0], value: U32Bytes::new(LE, 0), section_number: U16Bytes::new(LE, 0), typ: U16Bytes::new(LE, 0), storage_class: IMAGE_SYM_CLASS_NULL, number_of_aux_symbols: 0, }, ]; for table in &symbol_table { buffer.extend_from_slice(bytes_of(table)); } // __imp_ String Table Self::write_string_table( &mut buffer, &[ &format!("{}{}", prefix, sym), &format!("{}{}", prefix, weak), ], ); ArchiveMember { name: self.import_name.to_string(), data: buffer, symbols: Vec::new(), } } } implib-0.3.3/tests/python39.def000064400000000000000000001051541046102023000144300ustar 00000000000000; ; Definition file of python39.dll ; Automatic generated by gendef ; written by Kai Tietz 2008 ; LIBRARY "python39.dll" EXPORTS PyAST_CompileEx PyAST_CompileObject PyAST_FromNode PyAST_FromNodeObject PyAST_Validate PyArena_AddPyObject PyArena_Free PyArena_Malloc PyArena_New PyArg_Parse PyArg_ParseTuple PyArg_ParseTupleAndKeywords PyArg_UnpackTuple PyArg_VaParse PyArg_VaParseTupleAndKeywords PyArg_ValidateKeywordArguments PyAsyncGen_New PyAsyncGen_Type DATA PyBaseObject_Type DATA PyBool_FromLong PyBool_Type DATA PyBuffer_FillContiguousStrides PyBuffer_FillInfo PyBuffer_FromContiguous PyBuffer_GetPointer PyBuffer_IsContiguous PyBuffer_Release PyBuffer_SizeFromFormat PyBuffer_ToContiguous PyByteArrayIter_Type DATA PyByteArray_AsString PyByteArray_Concat PyByteArray_FromObject PyByteArray_FromStringAndSize PyByteArray_Resize PyByteArray_Size PyByteArray_Type DATA PyBytesIter_Type DATA PyBytes_AsString PyBytes_AsStringAndSize PyBytes_Concat PyBytes_ConcatAndDel PyBytes_DecodeEscape PyBytes_FromFormat PyBytes_FromFormatV PyBytes_FromObject PyBytes_FromString PyBytes_FromStringAndSize PyBytes_Repr PyBytes_Size PyBytes_Type DATA PyCFunction_Call PyCFunction_GetFlags PyCFunction_GetFunction PyCFunction_GetSelf PyCFunction_NewEx PyCFunction_Type DATA PyCMethod_New PyCMethod_Type DATA PyCallIter_New PyCallIter_Type DATA PyCallable_Check PyCapsule_GetContext PyCapsule_GetDestructor PyCapsule_GetName PyCapsule_GetPointer PyCapsule_Import PyCapsule_IsValid PyCapsule_New PyCapsule_SetContext PyCapsule_SetDestructor PyCapsule_SetName PyCapsule_SetPointer PyCapsule_Type DATA PyCell_Get PyCell_New PyCell_Set PyCell_Type DATA PyClassMethodDescr_Type DATA PyClassMethod_New PyClassMethod_Type DATA PyCode_Addr2Line PyCode_New PyCode_NewEmpty PyCode_NewWithPosOnlyArgs PyCode_Optimize PyCode_Type DATA PyCodec_BackslashReplaceErrors PyCodec_Decode PyCodec_Decoder PyCodec_Encode PyCodec_Encoder PyCodec_IgnoreErrors PyCodec_IncrementalDecoder PyCodec_IncrementalEncoder PyCodec_KnownEncoding PyCodec_LookupError PyCodec_NameReplaceErrors PyCodec_Register PyCodec_RegisterError PyCodec_ReplaceErrors PyCodec_StreamReader PyCodec_StreamWriter PyCodec_StrictErrors PyCodec_XMLCharRefReplaceErrors PyCompile_OpcodeStackEffect PyCompile_OpcodeStackEffectWithJump PyComplex_AsCComplex PyComplex_FromCComplex PyComplex_FromDoubles PyComplex_ImagAsDouble PyComplex_RealAsDouble PyComplex_Type DATA PyConfig_Clear PyConfig_InitIsolatedConfig PyConfig_InitPythonConfig PyConfig_Read PyConfig_SetArgv PyConfig_SetBytesArgv PyConfig_SetBytesString PyConfig_SetString PyConfig_SetWideStringList PyContextToken_Type DATA PyContextVar_Get PyContextVar_New PyContextVar_Reset PyContextVar_Set PyContextVar_Type DATA PyContext_Copy PyContext_CopyCurrent PyContext_Enter PyContext_Exit PyContext_New PyContext_Type DATA PyCoro_New PyCoro_Type DATA PyDescr_NewClassMethod PyDescr_NewGetSet PyDescr_NewMember PyDescr_NewMethod PyDescr_NewWrapper PyDictItems_Type DATA PyDictIterItem_Type DATA PyDictIterKey_Type DATA PyDictIterValue_Type DATA PyDictKeys_Type DATA PyDictProxy_New PyDictProxy_Type DATA PyDictRevIterItem_Type DATA PyDictRevIterKey_Type DATA PyDictRevIterValue_Type DATA PyDictValues_Type DATA PyDict_Clear PyDict_Contains PyDict_Copy PyDict_DelItem PyDict_DelItemString PyDict_GetItem PyDict_GetItemString PyDict_GetItemWithError PyDict_Items PyDict_Keys PyDict_Merge PyDict_MergeFromSeq2 PyDict_New PyDict_Next PyDict_SetDefault PyDict_SetItem PyDict_SetItemString PyDict_Size PyDict_Type DATA PyDict_Update PyDict_Values PyEllipsis_Type DATA PyEnum_Type DATA PyErr_BadArgument PyErr_BadInternalCall PyErr_CheckSignals PyErr_Clear PyErr_Display PyErr_ExceptionMatches PyErr_Fetch PyErr_Format PyErr_FormatV PyErr_GetExcInfo PyErr_GivenExceptionMatches PyErr_NewException PyErr_NewExceptionWithDoc PyErr_NoMemory PyErr_NormalizeException PyErr_Occurred PyErr_Print PyErr_PrintEx PyErr_ProgramText PyErr_ProgramTextObject PyErr_ResourceWarning PyErr_Restore PyErr_SetExcFromWindowsErr PyErr_SetExcFromWindowsErrWithFilename PyErr_SetExcFromWindowsErrWithFilenameObject PyErr_SetExcFromWindowsErrWithFilenameObjects PyErr_SetExcFromWindowsErrWithUnicodeFilename PyErr_SetExcInfo PyErr_SetFromErrno PyErr_SetFromErrnoWithFilename PyErr_SetFromErrnoWithFilenameObject PyErr_SetFromErrnoWithFilenameObjects PyErr_SetFromErrnoWithUnicodeFilename PyErr_SetFromWindowsErr PyErr_SetFromWindowsErrWithFilename PyErr_SetFromWindowsErrWithUnicodeFilename PyErr_SetImportError PyErr_SetImportErrorSubclass PyErr_SetInterrupt PyErr_SetNone PyErr_SetObject PyErr_SetString PyErr_SyntaxLocation PyErr_SyntaxLocationEx PyErr_SyntaxLocationObject PyErr_WarnEx PyErr_WarnExplicit PyErr_WarnExplicitFormat PyErr_WarnExplicitObject PyErr_WarnFormat PyErr_WriteUnraisable PyEval_AcquireLock PyEval_AcquireThread PyEval_CallFunction PyEval_CallMethod PyEval_CallObjectWithKeywords PyEval_EvalCode PyEval_EvalCodeEx PyEval_EvalFrame PyEval_EvalFrameEx PyEval_GetBuiltins PyEval_GetFrame PyEval_GetFuncDesc PyEval_GetFuncName PyEval_GetGlobals PyEval_GetLocals PyEval_InitThreads PyEval_MergeCompilerFlags PyEval_ReleaseLock PyEval_ReleaseThread PyEval_RestoreThread PyEval_SaveThread PyEval_SetProfile PyEval_SetTrace PyEval_ThreadsInitialized PyExc_ArithmeticError DATA PyExc_AssertionError DATA PyExc_AttributeError DATA PyExc_BaseException DATA PyExc_BlockingIOError DATA PyExc_BrokenPipeError DATA PyExc_BufferError DATA PyExc_BytesWarning DATA PyExc_ChildProcessError DATA PyExc_ConnectionAbortedError DATA PyExc_ConnectionError DATA PyExc_ConnectionRefusedError DATA PyExc_ConnectionResetError DATA PyExc_DeprecationWarning DATA PyExc_EOFError DATA PyExc_EnvironmentError DATA PyExc_Exception DATA PyExc_FileExistsError DATA PyExc_FileNotFoundError DATA PyExc_FloatingPointError DATA PyExc_FutureWarning DATA PyExc_GeneratorExit DATA PyExc_IOError DATA PyExc_ImportError DATA PyExc_ImportWarning DATA PyExc_IndentationError DATA PyExc_IndexError DATA PyExc_InterruptedError DATA PyExc_IsADirectoryError DATA PyExc_KeyError DATA PyExc_KeyboardInterrupt DATA PyExc_LookupError DATA PyExc_MemoryError DATA PyExc_ModuleNotFoundError DATA PyExc_NameError DATA PyExc_NotADirectoryError DATA PyExc_NotImplementedError DATA PyExc_OSError DATA PyExc_OverflowError DATA PyExc_PendingDeprecationWarning DATA PyExc_PermissionError DATA PyExc_ProcessLookupError DATA PyExc_RecursionError DATA PyExc_ReferenceError DATA PyExc_ResourceWarning DATA PyExc_RuntimeError DATA PyExc_RuntimeWarning DATA PyExc_StopAsyncIteration DATA PyExc_StopIteration DATA PyExc_SyntaxError DATA PyExc_SyntaxWarning DATA PyExc_SystemError DATA PyExc_SystemExit DATA PyExc_TabError DATA PyExc_TimeoutError DATA PyExc_TypeError DATA PyExc_UnboundLocalError DATA PyExc_UnicodeDecodeError DATA PyExc_UnicodeEncodeError DATA PyExc_UnicodeError DATA PyExc_UnicodeTranslateError DATA PyExc_UnicodeWarning DATA PyExc_UserWarning DATA PyExc_ValueError DATA PyExc_Warning DATA PyExc_WindowsError DATA PyExc_ZeroDivisionError DATA PyExceptionClass_Name PyException_GetCause PyException_GetContext PyException_GetTraceback PyException_SetCause PyException_SetContext PyException_SetTraceback PyFile_FromFd PyFile_GetLine PyFile_NewStdPrinter PyFile_OpenCode PyFile_OpenCodeObject PyFile_SetOpenCodeHook PyFile_WriteObject PyFile_WriteString PyFilter_Type DATA PyFloat_AsDouble PyFloat_FromDouble PyFloat_FromString PyFloat_GetInfo PyFloat_GetMax PyFloat_GetMin PyFloat_Type DATA PyFrame_BlockPop PyFrame_BlockSetup PyFrame_FastToLocals PyFrame_FastToLocalsWithError PyFrame_GetBack PyFrame_GetCode PyFrame_GetLineNumber PyFrame_LocalsToFast PyFrame_New PyFrame_Type DATA PyFrozenSet_New PyFrozenSet_Type DATA PyFunction_GetAnnotations PyFunction_GetClosure PyFunction_GetCode PyFunction_GetDefaults PyFunction_GetGlobals PyFunction_GetKwDefaults PyFunction_GetModule PyFunction_New PyFunction_NewWithQualName PyFunction_SetAnnotations PyFunction_SetClosure PyFunction_SetDefaults PyFunction_SetKwDefaults PyFunction_Type DATA PyFuture_FromAST PyFuture_FromASTObject PyGC_Collect PyGILState_Check PyGILState_Ensure PyGILState_GetThisThreadState PyGILState_Release PyGen_New PyGen_NewWithQualName PyGen_Type DATA PyGetSetDescr_Type DATA PyHash_GetFuncDef PyImport_AddModule PyImport_AddModuleObject PyImport_AppendInittab PyImport_ExecCodeModule PyImport_ExecCodeModuleEx PyImport_ExecCodeModuleObject PyImport_ExecCodeModuleWithPathnames PyImport_ExtendInittab PyImport_FrozenModules DATA PyImport_GetImporter PyImport_GetMagicNumber PyImport_GetMagicTag PyImport_GetModule PyImport_GetModuleDict PyImport_Import PyImport_ImportFrozenModule PyImport_ImportFrozenModuleObject PyImport_ImportModule PyImport_ImportModuleLevel PyImport_ImportModuleLevelObject PyImport_ImportModuleNoBlock PyImport_Inittab DATA PyImport_ReloadModule PyIndex_Check PyInstanceMethod_Function PyInstanceMethod_New PyInstanceMethod_Type DATA PyInterpreterState_Clear PyInterpreterState_Delete PyInterpreterState_Get PyInterpreterState_GetDict PyInterpreterState_GetID PyInterpreterState_Head PyInterpreterState_Main PyInterpreterState_New PyInterpreterState_Next PyInterpreterState_ThreadHead PyIter_Check PyIter_Next PyListIter_Type DATA PyListRevIter_Type DATA PyList_Append PyList_AsTuple PyList_GetItem PyList_GetSlice PyList_Insert PyList_New PyList_Reverse PyList_SetItem PyList_SetSlice PyList_Size PyList_Sort PyList_Type DATA PyLongRangeIter_Type DATA PyLong_AsDouble PyLong_AsLong PyLong_AsLongAndOverflow PyLong_AsLongLong PyLong_AsLongLongAndOverflow PyLong_AsSize_t PyLong_AsSsize_t PyLong_AsUnsignedLong PyLong_AsUnsignedLongLong PyLong_AsUnsignedLongLongMask PyLong_AsUnsignedLongMask PyLong_AsVoidPtr PyLong_FromDouble PyLong_FromLong PyLong_FromLongLong PyLong_FromSize_t PyLong_FromSsize_t PyLong_FromString PyLong_FromUnicode PyLong_FromUnicodeObject PyLong_FromUnsignedLong PyLong_FromUnsignedLongLong PyLong_FromVoidPtr PyLong_GetInfo PyLong_Type DATA PyMap_Type DATA PyMapping_Check PyMapping_GetItemString PyMapping_HasKey PyMapping_HasKeyString PyMapping_Items PyMapping_Keys PyMapping_Length PyMapping_SetItemString PyMapping_Size PyMapping_Values PyMarshal_ReadLastObjectFromFile PyMarshal_ReadLongFromFile PyMarshal_ReadObjectFromFile PyMarshal_ReadObjectFromString PyMarshal_ReadShortFromFile PyMarshal_WriteLongToFile PyMarshal_WriteObjectToFile PyMarshal_WriteObjectToString PyMem_Calloc PyMem_Free PyMem_GetAllocator PyMem_Malloc PyMem_RawCalloc PyMem_RawFree PyMem_RawMalloc PyMem_RawRealloc PyMem_Realloc PyMem_SetAllocator PyMem_SetupDebugHooks PyMemberDescr_Type DATA PyMember_GetOne PyMember_SetOne PyMemoryView_FromBuffer PyMemoryView_FromMemory PyMemoryView_FromObject PyMemoryView_GetContiguous PyMemoryView_Type DATA PyMethodDescr_Type DATA PyMethod_Function PyMethod_New PyMethod_Self PyMethod_Type DATA PyModuleDef_Init PyModuleDef_Type DATA PyModule_AddFunctions PyModule_AddIntConstant PyModule_AddObject PyModule_AddStringConstant PyModule_AddType PyModule_Create2 PyModule_ExecDef PyModule_FromDefAndSpec2 PyModule_GetDef PyModule_GetDict PyModule_GetFilename PyModule_GetFilenameObject PyModule_GetName PyModule_GetNameObject PyModule_GetState PyModule_New PyModule_NewObject PyModule_SetDocString PyModule_Type DATA PyNode_AddChild PyNode_Compile PyNode_Free PyNode_ListTree PyNode_New PyNumber_Absolute PyNumber_Add PyNumber_And PyNumber_AsSsize_t PyNumber_Check PyNumber_Divmod PyNumber_Float PyNumber_FloorDivide PyNumber_InPlaceAdd PyNumber_InPlaceAnd PyNumber_InPlaceFloorDivide PyNumber_InPlaceLshift PyNumber_InPlaceMatrixMultiply PyNumber_InPlaceMultiply PyNumber_InPlaceOr PyNumber_InPlacePower PyNumber_InPlaceRemainder PyNumber_InPlaceRshift PyNumber_InPlaceSubtract PyNumber_InPlaceTrueDivide PyNumber_InPlaceXor PyNumber_Index PyNumber_Invert PyNumber_Long PyNumber_Lshift PyNumber_MatrixMultiply PyNumber_Multiply PyNumber_Negative PyNumber_Or PyNumber_Positive PyNumber_Power PyNumber_Remainder PyNumber_Rshift PyNumber_Subtract PyNumber_ToBase PyNumber_TrueDivide PyNumber_Xor PyODictItems_Type DATA PyODictIter_Type DATA PyODictKeys_Type DATA PyODictValues_Type DATA PyODict_DelItem PyODict_New PyODict_SetItem PyODict_Type DATA PyOS_AfterFork PyOS_FSPath PyOS_InitInterrupts PyOS_InputHook DATA PyOS_InterruptOccurred PyOS_Readline PyOS_ReadlineFunctionPointer DATA PyOS_double_to_string PyOS_getsig PyOS_mystricmp PyOS_mystrnicmp PyOS_setsig PyOS_snprintf PyOS_string_to_double PyOS_strtol PyOS_strtoul PyOS_vsnprintf PyObject_ASCII PyObject_AsCharBuffer PyObject_AsFileDescriptor PyObject_AsReadBuffer PyObject_AsWriteBuffer PyObject_Bytes PyObject_Call PyObject_CallFinalizer PyObject_CallFinalizerFromDealloc PyObject_CallFunction PyObject_CallFunctionObjArgs PyObject_CallMethod PyObject_CallMethodObjArgs PyObject_CallNoArgs PyObject_CallObject PyObject_Calloc PyObject_CheckBuffer PyObject_CheckReadBuffer PyObject_ClearWeakRefs PyObject_CopyData PyObject_DelItem PyObject_DelItemString PyObject_Dir PyObject_Format PyObject_Free PyObject_GC_Del PyObject_GC_IsFinalized PyObject_GC_IsTracked PyObject_GC_Track PyObject_GC_UnTrack PyObject_GET_WEAKREFS_LISTPTR PyObject_GenericGetAttr PyObject_GenericGetDict PyObject_GenericSetAttr PyObject_GenericSetDict PyObject_GetArenaAllocator PyObject_GetAttr PyObject_GetAttrString PyObject_GetBuffer PyObject_GetItem PyObject_GetIter PyObject_HasAttr PyObject_HasAttrString PyObject_Hash PyObject_HashNotImplemented PyObject_IS_GC PyObject_Init PyObject_InitVar PyObject_IsInstance PyObject_IsSubclass PyObject_IsTrue PyObject_Length PyObject_LengthHint PyObject_Malloc PyObject_Not PyObject_Print PyObject_Realloc PyObject_Repr PyObject_RichCompare PyObject_RichCompareBool PyObject_SelfIter PyObject_SetArenaAllocator PyObject_SetAttr PyObject_SetAttrString PyObject_SetItem PyObject_Size PyObject_Str PyObject_Type PyObject_VectorcallDict PyObject_VectorcallMethod PyParser_ASTFromFile PyParser_ASTFromFileObject PyParser_ASTFromString PyParser_ASTFromStringObject PyParser_ClearError PyParser_ParseFile PyParser_ParseFileFlags PyParser_ParseFileFlagsEx PyParser_ParseFileObject PyParser_ParseString PyParser_ParseStringFlags PyParser_ParseStringFlagsFilename PyParser_ParseStringFlagsFilenameEx PyParser_ParseStringObject PyParser_SetError PyParser_SimpleParseFile PyParser_SimpleParseFileFlags PyParser_SimpleParseString PyParser_SimpleParseStringFlags PyParser_SimpleParseStringFlagsFilename PyPegen_ASTFromFileObject PyPegen_ASTFromFilename PyPegen_ASTFromString PyPegen_ASTFromStringObject PyPickleBuffer_FromObject PyPickleBuffer_GetBuffer PyPickleBuffer_Release PyPickleBuffer_Type DATA PyPreConfig_InitIsolatedConfig PyPreConfig_InitPythonConfig PyProperty_Type DATA PyRangeIter_Type DATA PyRange_Type DATA PyReversed_Type DATA PyRun_AnyFile PyRun_AnyFileEx PyRun_AnyFileExFlags PyRun_AnyFileFlags PyRun_File PyRun_FileEx PyRun_FileExFlags PyRun_FileFlags PyRun_InteractiveLoop PyRun_InteractiveLoopFlags PyRun_InteractiveOne PyRun_InteractiveOneFlags PyRun_InteractiveOneObject PyRun_SimpleFile PyRun_SimpleFileEx PyRun_SimpleFileExFlags PyRun_SimpleString PyRun_SimpleStringFlags PyRun_String PyRun_StringFlags PySTEntry_Type DATA PyST_GetScope PySeqIter_New PySeqIter_Type DATA PySequence_Check PySequence_Concat PySequence_Contains PySequence_Count PySequence_DelItem PySequence_DelSlice PySequence_Fast PySequence_GetItem PySequence_GetSlice PySequence_In PySequence_InPlaceConcat PySequence_InPlaceRepeat PySequence_Index PySequence_Length PySequence_List PySequence_Repeat PySequence_SetItem PySequence_SetSlice PySequence_Size PySequence_Tuple PySetIter_Type DATA PySet_Add PySet_Clear PySet_Contains PySet_Discard PySet_New PySet_Pop PySet_Size PySet_Type DATA PySlice_AdjustIndices PySlice_GetIndices PySlice_GetIndicesEx PySlice_New PySlice_Type DATA PySlice_Unpack PyState_AddModule PyState_FindModule PyState_RemoveModule PyStaticMethod_New PyStaticMethod_Type DATA PyStatus_Error PyStatus_Exception PyStatus_Exit PyStatus_IsError PyStatus_IsExit PyStatus_NoMemory PyStatus_Ok PyStdPrinter_Type DATA PyStructSequence_GetItem PyStructSequence_InitType PyStructSequence_InitType2 PyStructSequence_New PyStructSequence_NewType PyStructSequence_SetItem PySuper_Type DATA PySymtable_Build PySymtable_BuildObject PySymtable_Free PySymtable_Lookup PySys_AddAuditHook PySys_AddWarnOption PySys_AddWarnOptionUnicode PySys_AddXOption PySys_Audit PySys_FormatStderr PySys_FormatStdout PySys_GetObject PySys_GetXOptions PySys_HasWarnOptions PySys_ResetWarnOptions PySys_SetArgv PySys_SetArgvEx PySys_SetObject PySys_SetPath PySys_WriteStderr PySys_WriteStdout PyThreadState_Clear PyThreadState_Delete PyThreadState_DeleteCurrent PyThreadState_Get PyThreadState_GetDict PyThreadState_GetFrame PyThreadState_GetID PyThreadState_GetInterpreter PyThreadState_New PyThreadState_Next PyThreadState_SetAsyncExc PyThreadState_Swap PyThread_GetInfo PyThread_ReInitTLS PyThread_acquire_lock PyThread_acquire_lock_timed PyThread_allocate_lock PyThread_create_key PyThread_delete_key PyThread_delete_key_value PyThread_exit_thread PyThread_free_lock PyThread_get_key_value PyThread_get_stacksize PyThread_get_thread_ident PyThread_get_thread_native_id PyThread_init_thread PyThread_release_lock PyThread_set_key_value PyThread_set_stacksize PyThread_start_new_thread PyThread_tss_alloc PyThread_tss_create PyThread_tss_delete PyThread_tss_free PyThread_tss_get PyThread_tss_is_created PyThread_tss_set PyToken_OneChar PyToken_ThreeChars PyToken_TwoChars PyTraceBack_Here PyTraceBack_Print PyTraceBack_Type DATA PyTraceMalloc_Track PyTraceMalloc_Untrack PyTupleIter_Type DATA PyTuple_GetItem PyTuple_GetSlice PyTuple_New PyTuple_Pack PyTuple_SetItem PyTuple_Size PyTuple_Type DATA PyType_ClearCache PyType_FromModuleAndSpec PyType_FromSpec PyType_FromSpecWithBases PyType_GenericAlloc PyType_GenericNew PyType_GetFlags PyType_GetModule PyType_GetModuleState PyType_GetSlot PyType_IsSubtype PyType_Modified PyType_Ready PyType_Type DATA PyUnicodeDecodeError_Create PyUnicodeDecodeError_GetEncoding PyUnicodeDecodeError_GetEnd PyUnicodeDecodeError_GetObject PyUnicodeDecodeError_GetReason PyUnicodeDecodeError_GetStart PyUnicodeDecodeError_SetEnd PyUnicodeDecodeError_SetReason PyUnicodeDecodeError_SetStart PyUnicodeEncodeError_Create PyUnicodeEncodeError_GetEncoding PyUnicodeEncodeError_GetEnd PyUnicodeEncodeError_GetObject PyUnicodeEncodeError_GetReason PyUnicodeEncodeError_GetStart PyUnicodeEncodeError_SetEnd PyUnicodeEncodeError_SetReason PyUnicodeEncodeError_SetStart PyUnicodeIter_Type DATA PyUnicodeTranslateError_Create PyUnicodeTranslateError_GetEnd PyUnicodeTranslateError_GetObject PyUnicodeTranslateError_GetReason PyUnicodeTranslateError_GetStart PyUnicodeTranslateError_SetEnd PyUnicodeTranslateError_SetReason PyUnicodeTranslateError_SetStart PyUnicode_Append PyUnicode_AppendAndDel PyUnicode_AsASCIIString PyUnicode_AsCharmapString PyUnicode_AsDecodedObject PyUnicode_AsDecodedUnicode PyUnicode_AsEncodedObject PyUnicode_AsEncodedString PyUnicode_AsEncodedUnicode PyUnicode_AsLatin1String PyUnicode_AsMBCSString PyUnicode_AsRawUnicodeEscapeString PyUnicode_AsUCS4 PyUnicode_AsUCS4Copy PyUnicode_AsUTF16String PyUnicode_AsUTF32String PyUnicode_AsUTF8 PyUnicode_AsUTF8AndSize PyUnicode_AsUTF8String PyUnicode_AsUnicode PyUnicode_AsUnicodeAndSize PyUnicode_AsUnicodeCopy PyUnicode_AsUnicodeEscapeString PyUnicode_AsWideChar PyUnicode_AsWideCharString PyUnicode_BuildEncodingMap PyUnicode_Compare PyUnicode_CompareWithASCIIString PyUnicode_Concat PyUnicode_Contains PyUnicode_CopyCharacters PyUnicode_Count PyUnicode_Decode PyUnicode_DecodeASCII PyUnicode_DecodeCharmap PyUnicode_DecodeCodePageStateful PyUnicode_DecodeFSDefault PyUnicode_DecodeFSDefaultAndSize PyUnicode_DecodeLatin1 PyUnicode_DecodeLocale PyUnicode_DecodeLocaleAndSize PyUnicode_DecodeMBCS PyUnicode_DecodeMBCSStateful PyUnicode_DecodeRawUnicodeEscape PyUnicode_DecodeUTF16 PyUnicode_DecodeUTF16Stateful PyUnicode_DecodeUTF32 PyUnicode_DecodeUTF32Stateful PyUnicode_DecodeUTF7 PyUnicode_DecodeUTF7Stateful PyUnicode_DecodeUTF8 PyUnicode_DecodeUTF8Stateful PyUnicode_DecodeUnicodeEscape PyUnicode_Encode PyUnicode_EncodeASCII PyUnicode_EncodeCharmap PyUnicode_EncodeCodePage PyUnicode_EncodeDecimal PyUnicode_EncodeFSDefault PyUnicode_EncodeLatin1 PyUnicode_EncodeLocale PyUnicode_EncodeMBCS PyUnicode_EncodeRawUnicodeEscape PyUnicode_EncodeUTF16 PyUnicode_EncodeUTF32 PyUnicode_EncodeUTF7 PyUnicode_EncodeUTF8 PyUnicode_EncodeUnicodeEscape PyUnicode_FSConverter PyUnicode_FSDecoder PyUnicode_Fill PyUnicode_Find PyUnicode_FindChar PyUnicode_Format PyUnicode_FromEncodedObject PyUnicode_FromFormat PyUnicode_FromFormatV PyUnicode_FromKindAndData PyUnicode_FromObject PyUnicode_FromOrdinal PyUnicode_FromString PyUnicode_FromStringAndSize PyUnicode_FromUnicode PyUnicode_FromWideChar PyUnicode_GetDefaultEncoding PyUnicode_GetLength PyUnicode_GetMax PyUnicode_GetSize PyUnicode_InternFromString PyUnicode_InternImmortal PyUnicode_InternInPlace PyUnicode_IsIdentifier PyUnicode_Join PyUnicode_New PyUnicode_Partition PyUnicode_RPartition PyUnicode_RSplit PyUnicode_ReadChar PyUnicode_Replace PyUnicode_Resize PyUnicode_RichCompare PyUnicode_Split PyUnicode_Splitlines PyUnicode_Substring PyUnicode_Tailmatch PyUnicode_TransformDecimalToASCII PyUnicode_Translate PyUnicode_TranslateCharmap PyUnicode_Type DATA PyUnicode_WriteChar PyVectorcall_Call PyWeakref_GetObject PyWeakref_NewProxy PyWeakref_NewRef PyWideStringList_Append PyWideStringList_Insert PyWrapperDescr_Type DATA PyWrapper_New PyZip_Type DATA Py_AddPendingCall Py_AtExit Py_BuildValue Py_BytesMain Py_BytesWarningFlag DATA Py_CompileString Py_CompileStringExFlags Py_CompileStringFlags Py_CompileStringObject Py_DebugFlag DATA Py_DecRef Py_DecodeLocale Py_DontWriteBytecodeFlag DATA Py_EncodeLocale Py_EndInterpreter Py_EnterRecursiveCall Py_Exit Py_ExitStatusException Py_FatalError Py_FdIsInteractive Py_FileSystemDefaultEncodeErrors DATA Py_FileSystemDefaultEncoding DATA Py_Finalize Py_FinalizeEx Py_FrozenFlag DATA Py_GenericAlias Py_GenericAliasType DATA Py_GetArgcArgv Py_GetBuildInfo Py_GetCompiler Py_GetCopyright Py_GetExecPrefix Py_GetPath Py_GetPlatform Py_GetPrefix Py_GetProgramFullPath Py_GetProgramName Py_GetPythonHome Py_GetRecursionLimit Py_GetVersion Py_HasFileSystemDefaultEncoding DATA Py_HashRandomizationFlag DATA Py_IgnoreEnvironmentFlag DATA Py_IncRef Py_Initialize Py_InitializeEx Py_InitializeFromConfig Py_InspectFlag DATA Py_InteractiveFlag DATA Py_IsInitialized Py_IsolatedFlag DATA Py_LeaveRecursiveCall Py_LegacyWindowsFSEncodingFlag DATA Py_LegacyWindowsStdioFlag DATA Py_Main Py_MakePendingCalls Py_NewInterpreter Py_NoSiteFlag DATA Py_NoUserSiteDirectory DATA Py_OptimizeFlag DATA Py_PreInitialize Py_PreInitializeFromArgs Py_PreInitializeFromBytesArgs Py_QuietFlag DATA Py_ReprEnter Py_ReprLeave Py_RunMain Py_SetPath Py_SetProgramName Py_SetPythonHome Py_SetRecursionLimit Py_SetStandardStreamEncoding Py_SymtableString Py_SymtableStringObject Py_UNICODE_strcat Py_UNICODE_strchr Py_UNICODE_strcmp Py_UNICODE_strcpy Py_UNICODE_strlen Py_UNICODE_strncmp Py_UNICODE_strncpy Py_UNICODE_strrchr Py_UTF8Mode DATA Py_UnbufferedStdioFlag DATA Py_UniversalNewlineFgets Py_VaBuildValue Py_VerboseFlag DATA Py_hexdigits DATA _PyAST_GetDocString _PyAST_Optimize _PyAccu_Accumulate _PyAccu_Destroy _PyAccu_Finish _PyAccu_FinishAsList _PyAccu_Init _PyArg_BadArgument _PyArg_CheckPositional _PyArg_NoKeywords _PyArg_NoKwnames _PyArg_NoPositional _PyArg_ParseStack _PyArg_ParseStackAndKeywords _PyArg_ParseStackAndKeywords_SizeT _PyArg_ParseStack_SizeT _PyArg_ParseTupleAndKeywordsFast _PyArg_ParseTupleAndKeywordsFast_SizeT _PyArg_ParseTupleAndKeywords_SizeT _PyArg_ParseTuple_SizeT _PyArg_Parse_SizeT _PyArg_UnpackKeywords _PyArg_UnpackStack _PyArg_VaParseTupleAndKeywordsFast _PyArg_VaParseTupleAndKeywordsFast_SizeT _PyArg_VaParseTupleAndKeywords_SizeT _PyArg_VaParse_SizeT _PyArgv_AsWstrList _PyAsyncGenASend_Type DATA _PyAsyncGenAThrow_Type DATA _PyAsyncGenWrappedValue_Type DATA _PyByteArray_empty_string DATA _PyBytesIOBuffer_Type DATA _PyBytesWriter_Alloc _PyBytesWriter_Dealloc _PyBytesWriter_Finish _PyBytesWriter_Init _PyBytesWriter_Prepare _PyBytesWriter_Resize _PyBytesWriter_WriteBytes _PyBytes_DecodeEscape _PyBytes_FormatEx _PyBytes_FromHex _PyBytes_Join _PyBytes_Resize _PyCode_CheckLineNumber _PyCode_ConstantKey _PyCode_GetExtra _PyCode_SetExtra _PyCodecInfo_GetIncrementalDecoder _PyCodecInfo_GetIncrementalEncoder _PyCodec_DecodeText _PyCodec_EncodeText _PyCodec_Forget _PyCodec_Lookup _PyCodec_LookupTextEncoding _PyComplex_FormatAdvancedWriter _PyConfig_InitCompatConfig _PyContext_NewHamtForTests _PyCoroWrapper_Type DATA _PyCrossInterpreterData_Lookup _PyCrossInterpreterData_NewObject _PyCrossInterpreterData_RegisterClass _PyCrossInterpreterData_Release _PyDebugAllocatorStats _PyDictView_Intersect _PyDictView_New _PyDict_CheckConsistency _PyDict_Contains _PyDict_DebugMallocStats _PyDict_DelItemId _PyDict_DelItemIf _PyDict_DelItem_KnownHash _PyDict_GetItemId _PyDict_GetItemIdWithError _PyDict_GetItemStringWithError _PyDict_GetItem_KnownHash _PyDict_HasOnlyStringKeys _PyDict_MaybeUntrack _PyDict_MergeEx _PyDict_NewPresized _PyDict_Next _PyDict_Pop _PyDict_SetItemId _PyDict_SetItem_KnownHash _PyDict_SizeOf _PyErr_BadInternalCall _PyErr_ChainExceptions _PyErr_ChainStackItem _PyErr_CheckSignals _PyErr_CheckSignalsTstate _PyErr_Clear _PyErr_Display _PyErr_ExceptionMatches _PyErr_Fetch _PyErr_Format _PyErr_FormatFromCause _PyErr_FormatFromCauseTstate _PyErr_GetExcInfo _PyErr_GetTopmostException _PyErr_NoMemory _PyErr_NormalizeException _PyErr_Print _PyErr_Restore _PyErr_SetKeyError _PyErr_SetNone _PyErr_SetObject _PyErr_SetString _PyErr_TrySetFromCause _PyErr_WriteUnraisableMsg _PyEval_AddPendingCall _PyEval_CallTracing _PyEval_EvalCodeWithName _PyEval_EvalFrameDefault _PyEval_GetAsyncGenFinalizer _PyEval_GetAsyncGenFirstiter _PyEval_GetBuiltinId _PyEval_GetCoroutineOriginTrackingDepth _PyEval_GetSwitchInterval _PyEval_RequestCodeExtraIndex _PyEval_SetAsyncGenFinalizer _PyEval_SetAsyncGenFirstiter _PyEval_SetCoroutineOriginTrackingDepth _PyEval_SetProfile _PyEval_SetSwitchInterval _PyEval_SetTrace _PyEval_SignalAsyncExc _PyEval_SignalReceived _PyEval_SliceIndex _PyEval_SliceIndexNotNone _PyFloat_DebugMallocStats _PyFloat_FormatAdvancedWriter _PyFloat_Pack2 _PyFloat_Pack4 _PyFloat_Pack8 _PyFloat_Unpack2 _PyFloat_Unpack4 _PyFloat_Unpack8 _PyFrame_DebugMallocStats _PyFunction_Vectorcall _PyGC_CollectIfEnabled _PyGC_CollectNoFail _PyGC_InitState _PyGILState_GetInterpreterStateUnsafe _PyGILState_Reinit _PyGen_FetchStopIterationValue _PyGen_Finalize _PyGen_Send _PyGen_SetStopIterationValue _PyHamtItems_Type DATA _PyHamtKeys_Type DATA _PyHamtValues_Type DATA _PyHamt_ArrayNode_Type DATA _PyHamt_BitmapNode_Type DATA _PyHamt_CollisionNode_Type DATA _PyHamt_Type DATA _PyImport_AcquireLock _PyImport_FindExtensionObject _PyImport_FixupBuiltin _PyImport_FixupExtensionObject _PyImport_GetModuleId _PyImport_IsInitialized _PyImport_ReleaseLock _PyImport_SetModule _PyImport_SetModuleString _PyInterpreterID_LookUp _PyInterpreterID_New _PyInterpreterID_Type DATA _PyInterpreterState_DeleteExceptMain _PyInterpreterState_Enable _PyInterpreterState_GetConfig _PyInterpreterState_GetEvalFrameFunc _PyInterpreterState_GetIDObject _PyInterpreterState_GetMainModule _PyInterpreterState_IDDecref _PyInterpreterState_IDIncref _PyInterpreterState_IDInitref _PyInterpreterState_LookUpID _PyInterpreterState_RequireIDRef _PyInterpreterState_RequiresIDRef _PyInterpreterState_SetEvalFrameFunc _PyList_DebugMallocStats _PyList_Extend _PyLong_AsByteArray _PyLong_AsInt _PyLong_AsTime_t _PyLong_Copy _PyLong_DigitValue DATA _PyLong_DivmodNear _PyLong_Format _PyLong_FormatAdvancedWriter _PyLong_FormatBytesWriter _PyLong_FormatWriter _PyLong_Frexp _PyLong_FromByteArray _PyLong_FromBytes _PyLong_FromNbIndexOrNbInt _PyLong_FromNbInt _PyLong_FromTime_t _PyLong_GCD _PyLong_Lshift _PyLong_New _PyLong_NumBits _PyLong_One DATA _PyLong_Rshift _PyLong_Sign _PyLong_Size_t_Converter _PyLong_UnsignedInt_Converter _PyLong_UnsignedLongLong_Converter _PyLong_UnsignedLong_Converter _PyLong_UnsignedShort_Converter _PyLong_Zero DATA _PyManagedBuffer_Type DATA _PyMem_GetAllocatorName _PyMem_GetCurrentAllocatorName _PyMem_RawStrdup _PyMem_RawWcsdup _PyMem_SetDefaultAllocator _PyMem_SetupAllocators _PyMem_Strdup _PyMethodWrapper_Type DATA _PyModuleSpec_IsInitializing _PyModule_Clear _PyModule_ClearDict _PyModule_CreateInitialized _PyNamespace_New _PyNamespace_Type DATA _PyNode_SizeOf _PyNone_Type DATA _PyNotImplemented_Type DATA _PyOS_InterruptOccurred _PyOS_IsMainThread _PyOS_ReadlineTState DATA _PyOS_SigintEvent _PyOS_URandom _PyOS_URandomNonblock _PyObject_AssertFailed _PyObject_Call _PyObject_CallFunction_SizeT _PyObject_CallMethodId _PyObject_CallMethodIdObjArgs _PyObject_CallMethodId_SizeT _PyObject_CallMethod_SizeT _PyObject_Call_Prepend _PyObject_CheckConsistency _PyObject_CheckCrossInterpreterData _PyObject_DebugMallocStats _PyObject_DebugTypeStats _PyObject_Dump _PyObject_FastCallDictTstate _PyObject_FunctionStr _PyObject_GC_Calloc _PyObject_GC_Malloc _PyObject_GC_New _PyObject_GC_NewVar _PyObject_GC_Resize _PyObject_GenericGetAttrWithDict _PyObject_GenericSetAttrWithDict _PyObject_GetAttrId _PyObject_GetCrossInterpreterData _PyObject_GetDictPtr _PyObject_GetMethod _PyObject_HasAttrId _PyObject_HasLen _PyObject_IsAbstract _PyObject_IsFreed _PyObject_LookupAttr _PyObject_LookupAttrId _PyObject_LookupSpecial _PyObject_MakeTpCall _PyObject_New _PyObject_NewVar _PyObject_NextNotImplemented _PyObject_RealIsInstance _PyObject_RealIsSubclass _PyObject_SetAttrId _PyParser_Grammar DATA _PyParser_TokenNames DATA _PyPreConfig_InitCompatConfig _PyRuntime DATA _PyRuntimeState_Fini _PyRuntimeState_Init _PyRuntime_Finalize _PyRuntime_Initialize _PySequence_BytesToCharpArray _PySequence_IterSearch _PySet_Dummy DATA _PySet_NextEntry _PySet_Update _PySignal_AfterFork _PySlice_FromIndices _PySlice_GetLongIndices _PyStack_AsDict _PyState_AddModule _PySys_GetObjectId _PySys_GetSizeOf _PySys_SetObjectId _PyThreadState_DeleteCurrent _PyThreadState_DeleteExcept _PyThreadState_GetDict _PyThreadState_Init _PyThreadState_Prealloc _PyThreadState_Swap _PyThreadState_UncheckedGet _PyThread_CurrentFrames _PyTime_AsMicroseconds _PyTime_AsMilliseconds _PyTime_AsNanosecondsObject _PyTime_AsSecondsDouble _PyTime_AsTimeval _PyTime_AsTimevalTime_t _PyTime_AsTimeval_noraise _PyTime_FromMillisecondsObject _PyTime_FromNanoseconds _PyTime_FromNanosecondsObject _PyTime_FromSeconds _PyTime_FromSecondsObject _PyTime_GetMonotonicClock _PyTime_GetMonotonicClockWithInfo _PyTime_GetPerfCounter _PyTime_GetPerfCounterWithInfo _PyTime_GetSystemClock _PyTime_GetSystemClockWithInfo _PyTime_Init _PyTime_MulDiv _PyTime_ObjectToTime_t _PyTime_ObjectToTimespec _PyTime_ObjectToTimeval _PyTime_gmtime _PyTime_localtime _PyTraceMalloc_GetTraceback _PyTraceMalloc_NewReference _PyTraceback_Add _PyTrash_begin _PyTrash_deposit_object _PyTrash_destroy_chain _PyTrash_end _PyTrash_thread_deposit_object _PyTrash_thread_destroy_chain _PyTuple_DebugMallocStats _PyTuple_MaybeUntrack _PyTuple_Resize _PyType_CalculateMetaclass _PyType_CheckConsistency _PyType_GetDocFromInternalDoc _PyType_GetTextSignatureFromInternalDoc _PyType_Lookup _PyType_LookupId _PyType_Name _PyUnicodeTranslateError_Create _PyUnicodeWriter_Dealloc _PyUnicodeWriter_Finish _PyUnicodeWriter_Init _PyUnicodeWriter_PrepareInternal _PyUnicodeWriter_PrepareKindInternal _PyUnicodeWriter_WriteASCIIString _PyUnicodeWriter_WriteChar _PyUnicodeWriter_WriteLatin1String _PyUnicodeWriter_WriteStr _PyUnicodeWriter_WriteSubstring _PyUnicode_AsASCIIString _PyUnicode_AsLatin1String _PyUnicode_AsUTF8String _PyUnicode_AsUnicode _PyUnicode_CheckConsistency _PyUnicode_Copy _PyUnicode_DecodeRawUnicodeEscapeStateful _PyUnicode_DecodeUnicodeEscapeInternal _PyUnicode_DecodeUnicodeEscapeStateful _PyUnicode_EQ _PyUnicode_EncodeCharmap _PyUnicode_EncodeUTF16 _PyUnicode_EncodeUTF32 _PyUnicode_EncodeUTF7 _PyUnicode_EqualToASCIIId _PyUnicode_EqualToASCIIString _PyUnicode_FastCopyCharacters _PyUnicode_FastFill _PyUnicode_FindMaxChar _PyUnicode_FormatAdvancedWriter _PyUnicode_FormatLong _PyUnicode_FromASCII _PyUnicode_FromId _PyUnicode_InsertThousandsGrouping _PyUnicode_IsAlpha _PyUnicode_IsCaseIgnorable _PyUnicode_IsCased _PyUnicode_IsDecimalDigit _PyUnicode_IsDigit _PyUnicode_IsLinebreak _PyUnicode_IsLowercase _PyUnicode_IsNumeric _PyUnicode_IsPrintable _PyUnicode_IsTitlecase _PyUnicode_IsUppercase _PyUnicode_IsWhitespace _PyUnicode_IsXidContinue _PyUnicode_IsXidStart _PyUnicode_JoinArray _PyUnicode_Ready _PyUnicode_ScanIdentifier _PyUnicode_ToDecimalDigit _PyUnicode_ToDigit _PyUnicode_ToFoldedFull _PyUnicode_ToLowerFull _PyUnicode_ToLowercase _PyUnicode_ToNumeric _PyUnicode_ToTitleFull _PyUnicode_ToTitlecase _PyUnicode_ToUpperFull _PyUnicode_ToUppercase _PyUnicode_TransformDecimalAndSpaceToASCII _PyUnicode_XStrip _PyWarnings_Init _PyWeakref_CallableProxyType DATA _PyWeakref_ClearRef _PyWeakref_GetWeakrefCount _PyWeakref_ProxyType DATA _PyWeakref_RefType DATA _PyWideStringList_AsList _PyWideStringList_Clear _PyWideStringList_Copy _PyWideStringList_Extend _PyWindowsConsoleIO_Type DATA _Py_BreakPoint _Py_BuildValue_SizeT _Py_CheckFunctionResult _Py_CheckRecursionLimit DATA _Py_CheckRecursiveCall _Py_ClearArgcArgv _Py_ClearStandardStreamEncoding _Py_CoerceLegacyLocale _Py_Dealloc _Py_DecodeLocaleEx _Py_DecodeUTF8Ex _Py_DecodeUTF8_surrogateescape _Py_DisplaySourceLine _Py_EllipsisObject DATA _Py_EncodeLocaleEx _Py_EncodeLocaleRaw _Py_EncodeUTF8Ex _Py_FalseStruct DATA _Py_FatalErrorFormat _Py_FatalErrorFunc _Py_FatalError_TstateNULL _Py_FreeCharPArray _Py_GetAllocatedBlocks _Py_GetConfig _Py_GetConfigsAsDict _Py_GetEnv _Py_GetErrorHandler _Py_GetForceASCII _Py_GetLocaleconvNumeric _Py_HandleSystemExit _Py_HashBytes _Py_HashDouble _Py_HashPointer _Py_HashPointerRaw _Py_HashSecret DATA _Py_InitializeMain _Py_IsCoreInitialized _Py_IsFinalizing _Py_IsLocaleCoercionTarget _Py_LegacyLocaleDetected _Py_Mangle _Py_NewInterpreter _Py_NewReference _Py_NoneStruct DATA _Py_NotImplementedStruct DATA _Py_PackageContext DATA _Py_PreInitializeFromConfig _Py_PreInitializeFromPyArgv _Py_PyAtExit _Py_ResetForceASCII _Py_RestoreSignals _Py_SetLocaleFromEnv _Py_SetProgramFullPath _Py_SourceAsString _Py_SwappedOp DATA _Py_SymtableStringObjectFlags _Py_TrueStruct DATA _Py_UnhandledKeyboardInterrupt DATA _Py_VaBuildStack _Py_VaBuildStack_SizeT _Py_VaBuildValue_SizeT _Py_abspath _Py_add_one_to_index_C _Py_add_one_to_index_F _Py_ascii_whitespace DATA _Py_bit_length _Py_c_abs _Py_c_diff _Py_c_neg _Py_c_pow _Py_c_prod _Py_c_quot _Py_c_sum _Py_convert_optional_to_ssize_t _Py_ctype_table DATA _Py_ctype_tolower DATA _Py_ctype_toupper DATA _Py_device_encoding _Py_dg_dtoa _Py_dg_freedtoa _Py_dg_infinity _Py_dg_stdnan _Py_dg_strtod _Py_dup _Py_fopen _Py_fopen_obj _Py_fstat _Py_fstat_noraise _Py_get_env_flag _Py_get_inheritable _Py_get_xoption _Py_gitidentifier _Py_gitversion _Py_hashtable_clear _Py_hashtable_compare_direct _Py_hashtable_destroy _Py_hashtable_foreach _Py_hashtable_get _Py_hashtable_hash_ptr _Py_hashtable_new _Py_hashtable_new_full _Py_hashtable_set _Py_hashtable_size _Py_hashtable_steal _Py_open _Py_open_noraise _Py_parse_inf_or_nan _Py_path_config DATA _Py_read _Py_set_inheritable _Py_set_inheritable_async_safe _Py_stat _Py_str_to_int _Py_strhex _Py_strhex_bytes _Py_strhex_bytes_with_sep _Py_strhex_with_sep _Py_string_to_number_with_underscores _Py_tracemalloc_config DATA _Py_wfopen _Py_wgetcwd _Py_write _Py_write_noraise implib-0.3.3/tests/test_import_lib.rs000064400000000000000000000025051046102023000160140ustar 00000000000000use implib::{Flavor, ImportLibrary, MachineType}; #[cfg(feature = "msvc")] #[test] fn test_import_library_msvc_amd64() { let import_lib = ImportLibrary::new( include_str!("python39.def"), MachineType::AMD64, Flavor::Msvc, ) .unwrap(); let mut lib = std::fs::File::create("amd64-python39.lib").unwrap(); import_lib.write_to(&mut lib).unwrap(); } #[test] fn test_import_library_msvc_i386() { let import_lib = ImportLibrary::new( include_str!("python39.def"), MachineType::I386, Flavor::Msvc, ) .unwrap(); let mut lib = std::fs::File::create("i386-python39.lib").unwrap(); import_lib.write_to(&mut lib).unwrap(); } #[cfg(feature = "gnu")] #[test] fn test_import_library_gnu_amd64() { let import_lib = ImportLibrary::new( include_str!("python39.def"), MachineType::AMD64, Flavor::Gnu, ) .unwrap(); let mut lib = std::fs::File::create("amd64-python39.dll.a").unwrap(); import_lib.write_to(&mut lib).unwrap(); } #[cfg(feature = "gnu")] #[test] fn test_import_library_gnu_i386() { let import_lib = ImportLibrary::new(include_str!("python39.def"), MachineType::I386, Flavor::Gnu).unwrap(); let mut lib = std::fs::File::create("i386-python39.dll.a").unwrap(); import_lib.write_to(&mut lib).unwrap(); }