Skip to content

Commit e3aac4a

Browse files
committed
regression test for issue.
1 parent 51bb1da commit e3aac4a

File tree

8 files changed

+2090
-0
lines changed

8 files changed

+2090
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
-include ../../run-make-fulldeps/tools.mk
2+
3+
# How to run this
4+
# $ ./x.py clean
5+
# $ ./x.py test --target thumbv7m-none-eabi src/test/run-make
6+
7+
# The original target of interest was thumbv7em-none-eabi
8+
9+
# This is a test of a scenario that arose in issue rust-lang/rust#59535.
10+
#
11+
# What the user experienced: deleting a method use caused a link-time failure.
12+
#
13+
# What the cause was: At the time, incremental compilation would determine which
14+
# object files could be reused from a previous build based on, in part, 1. the
15+
# "greenness" of the modules in the current crate (i.e. which modules' contents
16+
# have changed since the incremental build) and 2. the current LTO import
17+
# information of the modules in the current crate.
18+
#
19+
# The problem was that the reused object file could have been optimized based on
20+
# LTO imports of a *previous* compile, not the current one. In other words, the
21+
# past LTO import information could have included more modules than what the
22+
# current LTO imports do, and those dependencies need to be respected when you
23+
# decide what object files to reuse.
24+
#
25+
# To make this more concrete: Here is the the high-level description of the
26+
# specific scenario from rust-lang/rust#59535:
27+
#
28+
# We had a call-graph like this: `[A] -> [B -> D] <- [C]`, where modules are in `[]`
29+
#
30+
# and the change between incremental builds was the `D <- C` link was removed.
31+
#
32+
# 1ST COMPILE: At the time of the first compile, LTO-optimization inlined the
33+
# code from `B` into `A`, so that in the object code for `A`, there was now a
34+
# direct link to the symbol for `D`. The LTO imports computed during this first
35+
# compile showed this directly: it said that `[A]` imported `B` and `D`.
36+
#
37+
# 2ND COMPIILE: But on the second compile, after the developer removed the call
38+
# `D <- C`, the Rust compiler itself determined that the `D` could be declared
39+
# as an internal definition, and then LLVM optimized the definition of `D` away
40+
# entirely. At the *same time*, the LTO imports computed during this second
41+
# compile reported by LLVM said that `[A]` *solely* imported `B`, and so the
42+
# Rust compiler's incremental reuse mechanism determined that we could reuse the
43+
# object code previously generated (during the 1st compile) for `[A]`. This
44+
# conclusion seemed valid to the compiler, because nothing changed about any
45+
# imports that it knew of for `[A]` based on the current compile.
46+
47+
### (Switch to rustup's nightly to observe bug)
48+
# RUSTC := rustc +nightly
49+
RUSTC := $(RUSTC_ORIGINAL)
50+
51+
OUT_DIR = $(TMPDIR)
52+
53+
INCREMENTAL_DIR = $(OUT_DIR)/incr
54+
RUBBLE1_OUT_DIR = $(OUT_DIR)/rubble1
55+
RUBBLE2_OUT_DIR = $(OUT_DIR)/rubble2
56+
57+
LINK_X_DIR := .
58+
59+
all:
60+
mkdir -p $(OUT_DIR)
61+
mkdir -p $(INCREMENTAL_DIR)
62+
mkdir -p $(RUBBLE1_OUT_DIR)
63+
mkdir -p $(RUBBLE2_OUT_DIR)
64+
$(RUSTC) --crate-name cortex_m_rt cortex-m-rt.rs --crate-type lib --emit=metadata,link -C opt-level=s --out-dir $(OUT_DIR) --target $(TARGET) -C link-arg=-Tlink.x.in -L $(LINK_X_DIR)
65+
$(RUSTC) --edition=2018 --crate-name nrf52810_hal nrf52810-hal.rs --crate-type lib --emit=metadata,link -C opt-level=s -C metadata=aa86958b67bf89f5 --out-dir $(OUT_DIR) --target $(TARGET) --extern cortex_m_rt=$(OUT_DIR)/libcortex_m_rt.rmeta -C link-arg=-Tlink.x.in -L $(LINK_X_DIR)
66+
cp rubble.rs.v1 $(TMPDIR)/rubble.rs
67+
$(RUSTC) --crate-name rubble $(TMPDIR)/rubble.rs --crate-type bin --emit=link -C opt-level=s --out-dir $(RUBBLE1_OUT_DIR) --target $(TARGET) -C incremental=$(INCREMENTAL_DIR) -L dependency=$(OUT_DIR) --extern nrf52810_hal=$(OUT_DIR)/libnrf52810_hal.rlib -C link-arg=-Tlink.x.in -L $(LINK_X_DIR) -C linker-flavor=ld.lld -C codegen-units=2
68+
cp rubble.rs.v2 $(TMPDIR)/rubble.rs
69+
$(RUSTC) --crate-name rubble $(TMPDIR)/rubble.rs --crate-type bin --emit=link -C opt-level=s --out-dir $(RUBBLE2_OUT_DIR) --target $(TARGET) -C incremental=$(INCREMENTAL_DIR) -L dependency=$(OUT_DIR) --extern nrf52810_hal=$(OUT_DIR)/libnrf52810_hal.rlib -C link-arg=-Tlink.x.in -L $(LINK_X_DIR) -C linker-flavor=ld.lld -C codegen-units=2
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![no_std]
2+
3+
#[link_section = ".vector_table.reset_vector"]
4+
#[no_mangle] pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
5+
6+
extern "Rust" { fn main() -> !; }
7+
8+
#[no_mangle] pub unsafe extern "C" fn Reset() -> ! { main() }
9+
10+
#[no_mangle] pub unsafe extern "C" fn DefaultHandler_() -> ! { loop { } }
11+
12+
#[link_section = ".vector_table.exceptions"]
13+
#[no_mangle] pub static __EXCEPTIONS: [usize; 14] = [0; 14];
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/* Sample device.x file */
2+
PROVIDE(WWDG = DefaultHandler);
3+
PROVIDE(PVD = DefaultHandler);
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/* # Developer notes
2+
3+
- Symbols that start with a double underscore (__) are considered "private"
4+
5+
- Symbols that start with a single underscore (_) are considered "semi-public"; they can be
6+
overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" {
7+
static mut __sbss }`).
8+
9+
- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a
10+
symbol if not dropped if it appears in or near the front of the linker arguments and "it's not
11+
needed" by any of the preceding objects (linker arguments)
12+
13+
- `PROVIDE` is used to provide default values that can be overridden by a user linker script
14+
15+
- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
16+
the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization
17+
routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see
18+
"Address (..) is out of bounds" in the disassembly produced by `objdump`.
19+
*/
20+
21+
/* Provides information about the memory layout of the device */
22+
/* This will be provided by the user (see `memory.x`) or by a Board Support Crate */
23+
INCLUDE memory.x
24+
25+
/* # Entry point = reset vector */
26+
ENTRY(Reset);
27+
EXTERN(__RESET_VECTOR); /* depends on the `Reset` symbol */
28+
29+
/* # Exception vectors */
30+
/* This is effectively weak aliasing at the linker level */
31+
/* The user can override any of these aliases by defining the corresponding symbol themselves (cf.
32+
the `exception!` macro) */
33+
EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */
34+
35+
EXTERN(DefaultHandler);
36+
37+
PROVIDE(NonMaskableInt = DefaultHandler);
38+
EXTERN(HardFaultTrampoline);
39+
PROVIDE(MemoryManagement = DefaultHandler);
40+
PROVIDE(BusFault = DefaultHandler);
41+
PROVIDE(UsageFault = DefaultHandler);
42+
PROVIDE(SecureFault = DefaultHandler);
43+
PROVIDE(SVCall = DefaultHandler);
44+
PROVIDE(DebugMonitor = DefaultHandler);
45+
PROVIDE(PendSV = DefaultHandler);
46+
PROVIDE(SysTick = DefaultHandler);
47+
48+
PROVIDE(DefaultHandler = DefaultHandler_);
49+
PROVIDE(HardFault = HardFault_);
50+
51+
/* # Interrupt vectors */
52+
EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */
53+
54+
/* # Pre-initialization function */
55+
/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function,
56+
then the function this points to will be called before the RAM is initialized. */
57+
PROVIDE(__pre_init = DefaultPreInit);
58+
59+
/* # Sections */
60+
SECTIONS
61+
{
62+
PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM));
63+
64+
/* ## Sections in FLASH */
65+
/* ### Vector table */
66+
.vector_table ORIGIN(FLASH) :
67+
{
68+
/* Initial Stack Pointer (SP) value */
69+
LONG(_stack_start);
70+
71+
/* Reset vector */
72+
KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */
73+
__reset_vector = .;
74+
75+
/* Exceptions */
76+
KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */
77+
__eexceptions = .;
78+
79+
/* Device specific interrupts */
80+
KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */
81+
} > FLASH
82+
83+
PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table));
84+
85+
/* ### .text */
86+
.text _stext :
87+
{
88+
*(.text .text.*);
89+
*(.HardFaultTrampoline);
90+
*(.HardFault.*);
91+
. = ALIGN(4);
92+
__etext = .;
93+
} > FLASH
94+
95+
/* ### .rodata */
96+
.rodata __etext : ALIGN(4)
97+
{
98+
*(.rodata .rodata.*);
99+
100+
/* 4-byte align the end (VMA) of this section.
101+
This is required by LLD to ensure the LMA of the following .data
102+
section will have the correct alignment. */
103+
. = ALIGN(4);
104+
__erodata = .;
105+
} > FLASH
106+
107+
/* ## Sections in RAM */
108+
/* ### .data */
109+
.data : AT(__erodata) ALIGN(4)
110+
{
111+
. = ALIGN(4);
112+
__sdata = .;
113+
*(.data .data.*);
114+
. = ALIGN(4); /* 4-byte align the end (VMA) of this section */
115+
__edata = .;
116+
} > RAM
117+
118+
/* LMA of .data */
119+
__sidata = LOADADDR(.data);
120+
121+
/* ### .bss */
122+
.bss : ALIGN(4)
123+
{
124+
. = ALIGN(4);
125+
__sbss = .;
126+
*(.bss .bss.*);
127+
. = ALIGN(4); /* 4-byte align the end (VMA) of this section */
128+
__ebss = .;
129+
} > RAM
130+
131+
/* ### .uninit */
132+
.uninit (NOLOAD) : ALIGN(4)
133+
{
134+
. = ALIGN(4);
135+
*(.uninit .uninit.*);
136+
. = ALIGN(4);
137+
} > RAM
138+
139+
/* Place the heap right after `.uninit` */
140+
. = ALIGN(4);
141+
__sheap = .;
142+
143+
/* ## .got */
144+
/* Dynamic relocations are unsupported. This section is only used to detect relocatable code in
145+
the input files and raise an error if relocatable code is found */
146+
.got (NOLOAD) :
147+
{
148+
KEEP(*(.got .got.*));
149+
}
150+
151+
/* ## Discarded sections */
152+
/DISCARD/ :
153+
{
154+
/* Unused exception related info that only wastes space */
155+
*(.ARM.exidx);
156+
*(.ARM.exidx.*);
157+
*(.ARM.extab.*);
158+
}
159+
}
160+
161+
/* Do not exceed this mark in the error messages below | */
162+
/* # Alignment checks */
163+
ASSERT(ORIGIN(FLASH) % 4 == 0, "
164+
ERROR(cortex-m-rt): the start of the FLASH region must be 4-byte aligned");
165+
166+
ASSERT(ORIGIN(RAM) % 4 == 0, "
167+
ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned");
168+
169+
ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, "
170+
BUG(cortex-m-rt): .data is not 4-byte aligned");
171+
172+
ASSERT(__sidata % 4 == 0, "
173+
BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned");
174+
175+
ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, "
176+
BUG(cortex-m-rt): .bss is not 4-byte aligned");
177+
178+
ASSERT(__sheap % 4 == 0, "
179+
BUG(cortex-m-rt): start of .heap is not 4-byte aligned");
180+
181+
/* # Position checks */
182+
183+
/* ## .vector_table */
184+
ASSERT(__reset_vector == ADDR(.vector_table) + 0x8, "
185+
BUG(cortex-m-rt): the reset vector is missing");
186+
187+
ASSERT(__eexceptions == ADDR(.vector_table) + 0x40, "
188+
BUG(cortex-m-rt): the exception vectors are missing");
189+
190+
ASSERT(SIZEOF(.vector_table) > 0x40, "
191+
ERROR(cortex-m-rt): The interrupt vectors are missing.
192+
Possible solutions, from most likely to less likely:
193+
- Link to a svd2rust generated device crate
194+
- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency
195+
may be enabling it)
196+
- Supply the interrupt handlers yourself. Check the documentation for details.");
197+
198+
/* ## .text */
199+
ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, "
200+
ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section
201+
Set _stext to an address greater than the end of .vector_table (See output of `nm`)");
202+
203+
ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), "
204+
ERROR(cortex-m-rt): The .text section must be placed inside the FLASH memory.
205+
Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'");
206+
207+
/* # Other checks */
208+
ASSERT(SIZEOF(.got) == 0, "
209+
ERROR(cortex-m-rt): .got section detected in the input object files
210+
Dynamic relocations are not supported. If you are linking to C code compiled using
211+
the 'cc' crate then modify your build script to compile the C code _without_
212+
the -fPIC flag. See the documentation of the `cc::Build.pic` method for details.");
213+
/* Do not exceed this mark in the error messages above | */
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* Linker script for the nRF52 - WITHOUT SOFT DEVICE */
2+
MEMORY
3+
{
4+
/* NOTE K = KiBi = 1024 bytes */
5+
FLASH : ORIGIN = 0x00000000, LENGTH = 192K
6+
RAM : ORIGIN = 0x20000000, LENGTH = 24K
7+
}
8+
9+
/* This is where the call stack will be allocated. */
10+
/* The stack is of the full descending type. */
11+
/* You may want to use this variable to locate the call stack and static
12+
variables in different memory regions. Below is shown the default value */
13+
/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */
14+
15+
/* You can use this symbol to customize the location of the .text section */
16+
/* If omitted the .text section will be placed right after the .vector_table
17+
section */
18+
/* This is required only on microcontrollers that store some configuration right
19+
after the vector table */
20+
/* _stext = ORIGIN(FLASH) + 0x400; */
21+
22+
/* Size of the heap (in bytes) */
23+
/* _heap_size = 1024; */

0 commit comments

Comments
 (0)