Change:
There's another bug in LLDB that causes it to segfault when reading archive files. This one has already been fixed in May () but it still exists in a lot of LLDB binaries out there. As far as I can tell, the bug occurs when a number of object files are stored in an archive and the last one ends at an uneven byte offset. This often seems to be the case for compressed byte-code archives in rlibs (maybe regular object files are always padded to an even size?). I'm currently compiling a possible workaround (pad the bytecode objects to an even size). It would be good if we could allow for rust code to be debugged with older LLDB versions too.
Update: Padding all archives entries to an even size seems to prevent the crash in LLDB and I have not seen any detrimental side effects to this approach so far. Any idea if the additional padding byte at the end of the compressed LLVM IR object in an archive could potentially lead to a problem?
I'm not familiar enough with the format of object files or bytecode files to know whether an extra byte or two at the end would corrupt them, but if it's working ok currently then I'd just ship it!
I doubt that the containing archive will care about the padding byte, but either the decompression algorithm or the LLVM bitcode reader could trip over it. The safe way to do this would probably be to prepend the actual size of the compressed data, so we always know how much data to pass to . I guess, I'll implement this and make a pull request.
Thinking about it, changing the format here is not as so trivial since the compiler must handle libs with the old format differently from libs with the new format. I see two options: (1) Just add the padding byte and hope for the best (2) Introduce some kind of header (including a version byte) before the compressed bitcode, in order to future-proof this against other changes to the encoding. I think I'd favor option (2).
Is this only a problem for the bytecode in archives, or is it also a problem for object files?
So far it has only occurred for bytecode. All object files I came across had a size dividable by 2. I don't know if that's a coincidence...
Interesting! I'm all for putting the bytecode in whatever format we want (it's already just a huge deflated stream)
I'll come up with something and then we can discuss the matter further when we have some code to look at in a PR, OK?
Sounds good to me!

Discussion:
use std::collections::HashSet; use std::io::{fs, TempDir, Command}; use std::io; <ins> use std::mem; </ins> use std::ptr; use std::str; use std::string::String;

use syntax::codemap::Span; use syntax::parse::token; <ins> // RLIB LLVM-BYTECODE OBJECT LAYOUT // Version 1 // Bytes    Data // 0..10    "RUST_OBJECT" encoded in ASCII // 11..14   format version as little-endian u32 // 15..22   size in bytes of deflate compressed LLVM bitcode as //          little-endian u64 // 23..     compressed LLVM bitcode // This is the "magic number" expected at the beginning of a LLVM bytecode // object in an rlib. pub static RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT"; // The version number this compiler will write to bytecode objects in rlibs pub static RLIB_BYTECODE_OBJECT_VERSION: u32 = 1; // The offset in bytes the bytecode object format version number can be found at pub static RLIB_BYTECODE_OBJECT_VERSION_OFFSET: uint = 11; // The offset in bytes the size of the compressed bytecode can be found at in // format version 1 pub static RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET: uint = RLIB_BYTECODE_OBJECT_VERSION_OFFSET + 4; // The offset in bytes the compressed LLVM bytecode can be found at in format // version 1 pub static RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: uint = RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8; </ins> #[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)] pub enum OutputType { OutputTypeBitcode,

// is never exactly 16 bytes long by adding a 16 byte extension to // it. This is to work around a bug in LLDB that would cause it to // crash if the name of a file in an archive was exactly 16 bytes. <del> let bc = obj_filename.with_extension("bc"); let bc_deflated = obj_filename.with_extension("bytecode.deflate"); match fs::File::open(&bc).read_to_end().and_then(|data| { fs::File::create(&bc_deflated) .write(match flate::deflate_bytes(data.as_slice()) { Some(compressed) => compressed, None => sess.fatal("failed to compress bytecode") }.as_slice()) }) { </del> <ins> let bc_filename = obj_filename.with_extension("bc"); let bc_deflated_filename = obj_filename.with_extension("bytecode.deflate"); let bc_data = match fs::File::open(&bc_filename).read_to_end() { Ok(buffer) => buffer, Err(e) => sess.fatal(format!("failed to read bytecode: {}", e).as_slice()) }; let bc_data_deflated = match flate::deflate_bytes(bc_data.as_slice()) { Some(compressed) => compressed, None => sess.fatal(format!("failed to compress bytecode from {}", bc_filename.display()).as_slice()) }; let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) { Ok(file) => file, Err(e) => { sess.fatal(format!("failed to create compressed bytecode  file: {}", e).as_slice()) } }; match write_rlib_bytecode_object_v1(&mut bc_file_deflated, bc_data_deflated.as_slice()) { </ins> Ok(()) => {} Err(e) => { sess.err(format!("failed to write compressed bytecode:  <del> {}", e).as_slice()); </del> <ins> {}", e).as_slice()); </ins> sess.abort_if_errors() } <del> } ab.add_file(&bc_deflated).unwrap(); remove(sess, &bc_deflated); </del> <ins> }; ab.add_file(&bc_deflated_filename).unwrap(); remove(sess, &bc_deflated_filename); </ins> if !sess.opts.cg.save_temps && !sess.opts.output_types.contains(&OutputTypeBitcode) { <del> remove(sess, &bc); </del> <ins> remove(sess, &bc_filename); </ins> } }

ab } <ins> fn write_rlib_bytecode_object_v1<T: Writer>(writer: &mut T, bc_data_deflated: &[u8]) -> ::std::io::IoResult<()> { let bc_data_deflated_size: u64 = bc_data_deflated.as_slice().len() as u64; try! { writer.write(RLIB_BYTECODE_OBJECT_MAGIC) }; try! { writer.write_le_u32(1) }; try! { writer.write_le_u64(bc_data_deflated_size) }; try! { writer.write(bc_data_deflated.as_slice()) }; let number_of_bytes_written_so_far = RLIB_BYTECODE_OBJECT_MAGIC.len() +                // magic id mem::size_of_val(&RLIB_BYTECODE_OBJECT_VERSION) + // version mem::size_of_val(&bc_data_deflated_size) +        // data size field bc_data_deflated_size as uint;                    // actual data // If the number of bytes written to the object so far is odd, add a // padding byte to make it even. This works around a crash bug in LLDB // (see issue #15950) if number_of_bytes_written_so_far % 2 == 1 { try! { writer.write_u8(0) }; } return Ok(()); } </ins> // Create a static archive // // This is essentially the same thing as an rlib, but it also involves adding

use libc; use flate; <ins> use std::mem; </ins> pub fn run(sess: &session::Session, llmod: ModuleRef, tm: TargetMachineRef, reachable: &[String]) { if sess.opts.cg.prefer_dynamic {

let file = path.filename_str().unwrap(); let file = file.slice(3, file.len() - 5); // chop off lib/.rlib debug!("reading {}", file); <del> let bc = time(sess.time_passes(), format!("read {}.bytecode.deflate", name).as_slice(), (), |_| { archive.read(format!("{}.bytecode.deflate", file).as_slice()) }); let bc = bc.expect("missing compressed bytecode in archive!"); let bc = time(sess.time_passes(), format!("inflate {}.bc", file).as_slice(), (), |_| { match flate::inflate_bytes(bc) { Some(bc) => bc, None => { sess.fatal(format!("failed to decompress  bc of `{}`", name).as_slice()) } } }); let ptr = bc.as_slice().as_ptr(); </del> <ins> let bc_encoded = time(sess.time_passes(), format!("read {}.bytecode.deflate", name).as_slice(), (), |_| { archive.read(format!("{}.bytecode.deflate", file).as_slice()) }); let bc_encoded = bc_encoded.expect("missing compressed bytecode in archive!"); let bc_extractor = if is_versioned_bytecode_format(bc_encoded) { |_| { // Read the version let version = extract_bytecode_format_version(bc_encoded); if version == 1 { // The only version existing so far let data_size = extract_compressed_bytecode_size_v1(bc_encoded); let compressed_data = bc_encoded.slice( link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET, link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET + data_size as uint); match flate::inflate_bytes(compressed_data) { Some(inflated) => inflated, None => { sess.fatal(format!("failed to decompress bc of `{}`", name).as_slice()) } } } else { sess.fatal(format!("Unsupported bytecode format version {}", version).as_slice()) } } } else { // the object must be in the old, pre-versioning format, so simply // inflate everything and let LLVM decide if it can make sense of it |_| { match flate::inflate_bytes(bc_encoded) { Some(bc) => bc, None => { sess.fatal(format!("failed to decompress bc of `{}`", name).as_slice()) } } } }; let bc_decoded = time(sess.time_passes(), format!("decode {}.bc", file).as_slice(), (), bc_extractor); let ptr = bc_decoded.as_slice().as_ptr(); </ins> debug!("linking {}", name); time(sess.time_passes(), format!("ll link {}", name).as_slice(),

|()| unsafe { if !llvm::LLVMRustLinkInExternalBitcode(llmod, ptr as *const libc::c_char, <del> bc.len() as libc::size_t) { </del> <ins> bc_decoded.len() as libc::size_t) { </ins> link::llvm_err(sess, format!("failed to load bc of `{}`", name.as_slice()));

} debug!("lto done"); } <ins> fn is_versioned_bytecode_format(bc: &[u8]) -> bool { let magic_id_byte_count = link::RLIB_BYTECODE_OBJECT_MAGIC.len(); return bc.len() > magic_id_byte_count && bc.slice(0, magic_id_byte_count) == link::RLIB_BYTECODE_OBJECT_MAGIC; } fn extract_bytecode_format_version(bc: &[u8]) -> u32 { return read_from_le_bytes::<u32>(bc, link::RLIB_BYTECODE_OBJECT_VERSION_OFFSET); } fn extract_compressed_bytecode_size_v1(bc: &[u8]) -> u64 { return read_from_le_bytes::<u64>(bc, link::RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET); } fn read_from_le_bytes<T: Int>(bytes: &[u8], position_in_bytes: uint) -> T { let byte_data = bytes.slice(position_in_bytes, position_in_bytes + mem::size_of::<T>()); let data = unsafe { *(byte_data.as_ptr() as *const T) }; Int::from_le(data) } </ins>