Skip to content

regression: UB with extern fn() -> ! and LTO #39253

Closed
@japaric

Description

@japaric

STR

$ cargo new --lib ub && cd $_

$ edit src/lib.rs && cat $_
#![feature(lang_items)]
#![no_std]

use core::ptr;

#[no_mangle]
pub unsafe fn _start() {
    extern "C" {
        fn main() -> !;
    }

    // Just to have "something" before `main`
    ptr::read_volatile(0x0 as *const usize);

    main();
}

// stubs
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}

#[lang = "panic_fmt"]
extern "C" fn panic_fmt() {}
$ edit examples/app.rs && cat $_
#![no_std]
#![no_main]

extern crate ub;

#[no_mangle]
pub fn main() -> ! {
    loop {}
}

Compiling with LTO produces:

$ cargo rustc --release --example app -- -C lto -C link-args=-nostartfiles

$ objdump -Cd target/release/examples/app
Disassembly of section .text:

00000000000002c0 <_start>:
 2c0:   48 8b 04 25 00 00 00    mov    0x0,%rax
 2c7:   00

Note that the infinite loop is missing. This program will execute stuff that's not in the .text section.

Whereas compiling without LTO produces:

$ cargo rustc --release --example app -- -C link-args=-nostartfiles

$ objdump -Cd target/release/examples/app
Disassembly of section .text:

00000000000002c0 <main>:
 2c0:   eb fe                   jmp    2c0 <main>
 2c2:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 2c9:   00 00 00
 2cc:   0f 1f 40 00             nopl   0x0(%rax)

00000000000002d0 <_start>:
 2d0:   50                      push   %rax
 2d1:   48 8b 04 25 00 00 00    mov    0x0,%rax
 2d8:   00
 2d9:   e8 e2 ff ff ff          callq  2c0 <main>

Both main and the infinite loop are preserved.

Meta

$ rustc -V
rustc 1.16.0-nightly (a52da95ce 2017-01-20)

This is a regression because nightly-2016-08-01 (I haven't bisected) preserved the infinite loop in presence of LTO.

$ cargo +nightly-2016-08-01 rustc --release --example app -- -C lto -C link-args=-nostartfiles

$ objdump -Cd target/release/examples/app
Disassembly of section .text:

00000000000002c0 <_start>:
 2c0:   48 8b 04 25 00 00 00    mov    0x0,%rax
 2c7:   00
 2c8:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
 2cf:   00
 2d0:   eb fe                   jmp    2d0 <_start+0x10>

Looking at the emitted object files, before they are sent to the linker, reveals more information:

$ cargo rustc --release --example app -- -C lto --emit=obj -C link-arg=-nostartfiles

$ objdump -Cd ./target/release/examples/app-e892a9c04992a722.o
Disassembly of section .text.main:

0000000000000000 <main>:
   0:   eb fe                   jmp    0 <main>

Disassembly of section .text._start:

0000000000000000 <_start>:
   0:   48 8b 04 25 00 00 00    mov    0x0,%rax
   7:   00

Note that today _start is simply not calling main at all.

$ cargo +nightly-2016-08-01 rustc --release --example app -- -C lto -C link-args=-nostartfiles --emit=obj

$ objdump -Cd ./target/release/examples/app.o
Disassembly of section .text.main:

0000000000000000 <main>:
   0:   eb fe                   jmp    0 <main>

Disassembly of section .text._start:

0000000000000000 <_start>:
   0:   48 8b 04 25 00 00 00    mov    0x0,%rax
   7:   00
   8:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
   f:   00
  10:   eb fe                   jmp    10 <_start+0x10>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions