aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/link/internal/ld/testdata
AgeCommit message (Collapse)Author
2026-02-17all: use LF line ending for C filesCherry Mui
For Go files, gofmt already converts CRLF line ending to LF. For C and assembly files, we don't enforce a format, but most files in tree are written with LF line ending, and the C toolchain can handle them file, even on Windows. Convert all to LF line ending for consistency. Will look into adding a test for them. Updates #9281. Change-Id: Idc0dc13f0ab90b8cd8ea118abf9cb195ec438fe7 Reviewed-on: https://go-review.googlesource.com/c/go/+/746220 Reviewed-by: Mark Freeman <markfreeman@google.com> Reviewed-by: Quim Muntal <quimmuntal@gmail.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2023-09-01cmd/link: add testcases for MethodByName(string literal).Dominique Lefevre
Change-Id: I96ea268ecceea75a24303526ed2f17c8a5e142c1 Reviewed-on: https://go-review.googlesource.com/c/go/+/522438 Reviewed-by: Cherry Mui <cherryyz@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Run-TryBot: Cherry Mui <cherryyz@google.com>
2023-03-23all: replace leading spaces with tabs in assemblyMichael Pratt
Most of these are one-off mistakes. Only one file was all spaces. Change-Id: I277c3ce4a4811aa4248c90676f66bc775ae8d062 Reviewed-on: https://go-review.googlesource.com/c/go/+/478976 Run-TryBot: Michael Pratt <mpratt@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Cherry Mui <cherryyz@google.com>
2023-02-22runtime: use explicit NOFRAME on linux/amd64qmuntal
This CL marks some linux assembly functions as NOFRAME to avoid relying on the implicit amd64 NOFRAME heuristic, where NOSPLIT functions without stack were also marked as NOFRAME. Updates #58378 Change-Id: I7792cff4f6e539bfa56c02868f2965088ca1975a Reviewed-on: https://go-review.googlesource.com/c/go/+/466316 Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Run-TryBot: Quim Muntal <quimmuntal@gmail.com> TryBot-Result: Gopher Robot <gobot@golang.org>
2023-02-06cmd/link: linker portion of dead map removalThan McIntosh
This patch contains the linker changes needed to enable deadcoding of large unreferenced map variables, in combination with a previous compiler change. We add a new cleanup function that runs just after deadcode that looks for relocations in "init" funcs that are weak, of type R_CALL (and siblings), and are targeting an unreachable function. If we find such a relocation, after checking to make sure it targets a map.init.XXX helper, we redirect the relocation to a point to a no-op routine ("mapinitnoop") in the runtime. Compilebench results for this change: │ out.base.txt │ out.wrap.txt │ │ sec/op │ sec/op vs base │ Template 218.6m ± 2% 221.1m ± 1% ~ (p=0.129 n=39) Unicode 180.5m ± 1% 178.9m ± 1% -0.93% (p=0.006 n=39) GoTypes 1.162 ± 1% 1.156 ± 1% ~ (p=0.850 n=39) Compiler 143.6m ± 1% 142.6m ± 1% ~ (p=0.743 n=39) SSA 8.698 ± 1% 8.719 ± 1% ~ (p=0.145 n=39) Flate 142.6m ± 1% 143.9m ± 3% ~ (p=0.287 n=39) GoParser 247.7m ± 1% 248.8m ± 1% ~ (p=0.265 n=39) Reflect 588.0m ± 1% 590.4m ± 1% ~ (p=0.269 n=39) Tar 198.5m ± 1% 201.3m ± 1% +1.38% (p=0.005 n=39) XML 259.1m ± 1% 260.0m ± 1% ~ (p=0.376 n=39) LinkCompiler 746.8m ± 2% 747.8m ± 1% ~ (p=0.706 n=39) ExternalLinkCompiler 1.906 ± 0% 1.902 ± 1% ~ (p=0.207 n=40) LinkWithoutDebugCompiler 522.4m ± 21% 471.1m ± 1% -9.81% (p=0.000 n=40) StdCmd 21.32 ± 0% 21.39 ± 0% +0.32% (p=0.035 n=40) geomean 609.2m 606.0m -0.53% │ out.base.txt │ out.wrap.txt │ │ user-sec/op │ user-sec/op vs base │ Template 401.9m ± 3% 406.9m ± 2% ~ (p=0.291 n=39) Unicode 191.9m ± 6% 196.1m ± 3% ~ (p=0.052 n=39) GoTypes 3.967 ± 3% 4.056 ± 1% ~ (p=0.099 n=39) Compiler 171.1m ± 3% 173.4m ± 3% ~ (p=0.415 n=39) SSA 30.04 ± 4% 30.25 ± 4% ~ (p=0.106 n=39) Flate 246.3m ± 3% 248.9m ± 4% ~ (p=0.499 n=39) GoParser 518.7m ± 1% 520.6m ± 2% ~ (p=0.531 n=39) Reflect 1.670 ± 1% 1.656 ± 2% ~ (p=0.137 n=39) Tar 352.7m ± 2% 360.3m ± 2% ~ (p=0.117 n=39) XML 528.8m ± 2% 521.1m ± 2% ~ (p=0.296 n=39) LinkCompiler 1.128 ± 2% 1.140 ± 2% ~ (p=0.324 n=39) ExternalLinkCompiler 2.165 ± 2% 2.147 ± 2% ~ (p=0.537 n=40) LinkWithoutDebugCompiler 484.2m ± 4% 490.7m ± 3% ~ (p=0.897 n=40) geomean 818.5m 825.1m +0.80% │ out.base.txt │ out.wrap.txt │ │ text-bytes │ text-bytes vs base │ HelloSize 766.0Ki ± 0% 766.0Ki ± 0% ~ (p=1.000 n=40) ¹ CmdGoSize 10.02Mi ± 0% 10.02Mi ± 0% -0.03% (n=40) geomean 2.738Mi 2.738Mi -0.01% ¹ all samples are equal │ out.base.txt │ out.wrap.txt │ │ data-bytes │ data-bytes vs base │ HelloSize 14.17Ki ± 0% 14.17Ki ± 0% ~ (p=1.000 n=40) ¹ CmdGoSize 308.3Ki ± 0% 298.5Ki ± 0% -3.19% (n=40) geomean 66.10Ki 65.04Ki -1.61% ¹ all samples are equal │ out.base.txt │ out.wrap.txt │ │ bss-bytes │ bss-bytes vs base │ HelloSize 197.3Ki ± 0% 197.3Ki ± 0% ~ (p=1.000 n=40) ¹ CmdGoSize 228.2Ki ± 0% 228.1Ki ± 0% -0.01% (n=40) geomean 212.2Ki 212.1Ki -0.01% ¹ all samples are equal │ out.base.txt │ out.wrap.txt │ │ exe-bytes │ exe-bytes vs base │ HelloSize 1.192Mi ± 0% 1.192Mi ± 0% +0.00% (p=0.000 n=40) CmdGoSize 14.85Mi ± 0% 14.83Mi ± 0% -0.09% (n=40) geomean 4.207Mi 4.205Mi -0.05% Also tested for any linker changes by benchmarking relink of k8s "kubelet"; no changes to speak of there. Updates #2559. Updates #36021. Updates #14840. Change-Id: I4cc5370b3f20679a1065aaaf87bdf2881e257631 Reviewed-on: https://go-review.googlesource.com/c/go/+/463395 Run-TryBot: Than McIntosh <thanm@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
2022-04-19cmd/link: faster algorithm for nosplit stack checking, better errorsAustin Clements
The linker performs a global analysis of all nosplit call chains to check they fit in the stack space ensured by splittable functions. That analysis has two problems right now: 1. It's inefficient. It performs a top-down analysis, starting with every nosplit function and the nosplit stack limit and walking *down* the call graph to compute how much stack remains at every call. As a result, it visits the same functions over and over, often with different remaining stack depths. This approach is historical: this check was originally written in C and this approach avoided the need for any interesting data structures. 2. If some call chain is over the limit, it only reports a single call chain. As a result, if the check does fail, you often wind up playing whack-a-mole by guessing where the problem is in the one chain, trying to reduce the stack size, and then seeing if the link works or reports a different path. This CL completely rewrites the nosplit stack check. It now uses a bottom-up analysis, computing the maximum stack height required by every function's call tree. This visits every function exactly once, making it much more efficient. It uses slightly more heap space for intermediate storage, but still very little in the scheme of the overall link. For example, when linking cmd/go, the new algorithm virtually eliminates the time spent in this pass, and reduces overall link time: │ before │ after │ │ sec/op │ sec/op vs base │ Dostkcheck 7.926m ± 4% 1.831m ± 6% -76.90% (p=0.000 n=20) TotalTime 301.3m ± 1% 296.4m ± 3% -1.62% (p=0.040 n=20) │ before │ after │ │ B/op │ B/op vs base │ Dostkcheck 40.00Ki ± 0% 212.15Ki ± 0% +430.37% (p=0.000 n=20) Most of this time is spent analyzing the runtime, so for larger binaries, the total time saved is roughly the same, and proportionally less of the overall link. If the new implementation finds an error, it redoes the analysis, switching to preferring quality of error reporting over performance. For error reporting, it computes stack depths top-down (like the old algorithm), and reports *all* paths that are over the stack limit, presented as a tree for compactness. For example, this is the output from a simple test case from test/nosplit with two over-limit paths from f1: main.f1: nosplit stack overflow main.f1 grows 768 bytes, calls main.f2 grows 56 bytes, calls main.f4 grows 48 bytes 80 bytes over limit grows 768 bytes, calls main.f3 grows 104 bytes 80 bytes over limit While we're here, we do a few nice cleanups: - We add a debug output flag, which will be useful for understanding what our nosplit chains look like and which ones are close to running over. - We move the implementation out of the fog of lib.go to its own file. - The implementation is generally more Go-like and less C-like. Change-Id: If1ab31197f5215475559b93695c44a01bd16e276 Reviewed-on: https://go-review.googlesource.com/c/go/+/398176 Run-TryBot: Austin Clements <austin@google.com> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
2021-03-24cmd/compile, cmd/link: use weak reference in itabCherry Zhang
When converting a type T to a non-empty interface I, we build the itab which contains the code pointers of the methods. Currently, this brings those methods live (if the itab is live), even if the interface method is never used. This CL changes the itab to use weak references, so the methods can be pruned if not otherwise live. Fixes #42421. Change-Id: Iee5de2ba11d603c5a102a2ba60440d839a7f9702 Reviewed-on: https://go-review.googlesource.com/c/go/+/268479 Trust: Cherry Zhang <cherryyz@google.com> Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Than McIntosh <thanm@google.com>
2021-03-15all: run gofmtPrajwal Koirala
Fixes #44980 Change-Id: Icef35319d1582d8367c8911e15d11b0224957327 GitHub-Last-Rev: 2113e97e837c1ef5de9ba6a7bd62db92e644c500 GitHub-Pull-Request: golang/go#45005 Reviewed-on: https://go-review.googlesource.com/c/go/+/301632 Reviewed-by: Ian Lance Taylor <iant@golang.org> Trust: Josh Bleecher Snyder <josharian@gmail.com>
2021-03-14cmd/link: regression test for issue #42484Alessandro Arzilli
Adds test to check that the compiler does not emit duplicate debug_line entries for the end of sequence address. Updates #42484 Change-Id: I3c5d1d606fcfd758aa1fd83ecc51d8edc054398b Reviewed-on: https://go-review.googlesource.com/c/go/+/270197 TryBot-Result: Go Bot <gobot@golang.org> Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com> Trust: Emmanuel Odeke <emmanuel@orijtech.com> Reviewed-by: Than McIntosh <thanm@google.com>
2020-11-16cmd/link/internal/ld: dedup shared libraries on openbsdJoel Sing
When linking internally on OpenBSD, dedup libraries treating versioned and unversioned libraries as equivalents. Versioned libraries are preferred and are retained over unversioned libraries. This avoids the situation where the use of cgo results in a DT_NEEDED for a versioned library (for example, libc.so.96.1), while a dynamic import specifies an unversioned library (for example, libc.so). Without deduplication this would result in two DT_NEEDED entries, causing a failure when ld.so attempts to load the Go binrary. Updates #36435 Fixes #39257 Change-Id: I4a4942f259dece01d97bb51df9e13d67c9f94d34 Reviewed-on: https://go-review.googlesource.com/c/go/+/249978 Trust: Joel Sing <joel@sing.id.au> Run-TryBot: Joel Sing <joel@sing.id.au> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-09-29cmd/link: retain only used interface methodsCherry Zhang
Currently, in the linker's deadcode pass, when an interface type is live, the linker thinks all its methods are live, and uses them to match methods on concrete types. The interface method may never be used, though. This CL changes it to only keep used interface methods, for matching concrete type methods. To do that, when an interface method is used, the compiler generates a mark relocation. The linker uses the marker relocations to mark used interface methods, and only the used ones. binary size before after cmd/compile 18887400 18812200 cmd/go 13470652 13470492 Change-Id: I3cfd9df4a53783330ba87735853f2a0ec3c42802 Reviewed-on: https://go-review.googlesource.com/c/go/+/256798 Trust: Cherry Zhang <cherryyz@google.com> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Jeremy Faller <jeremy@golang.org>
2020-09-28cmd/link: consider interface conversions only in reachable codeCherry Zhang
The linker prunes methods that are not directly reachable if the receiver type is never converted to interface. Currently, this "never" is too strong: it is invalidated even if the interface conversion is in an unreachable function. This CL improves it by only considering interface conversions in reachable code. To do that, we introduce a marker relocation R_USEIFACE, which marks the target symbol as UsedInIface if the source symbol is reached. binary size before after cmd/compile 18897528 18887400 cmd/go 13607372 13470652 Change-Id: I66c6b69eeff9ae02d84d2e6f2bc7f1b29dd53910 Reviewed-on: https://go-review.googlesource.com/c/go/+/256797 Trust: Cherry Zhang <cherryyz@google.com> Reviewed-by: Jeremy Faller <jeremy@golang.org> Reviewed-by: Than McIntosh <thanm@google.com>
2020-09-18cmd/link: propagate UsedInIface through method descriptorCherry Zhang
The linker prunes methods that are not directly reachable if the receiver type is never converted to interface. A type can be converted to interface using reflection through other types. The linker already takes this into consideration but it missed the case that the intermediate is a method descriptor. Handle this case. Change-Id: I590efc5da163c326db8d43583908a2ef67f65d9d Reviewed-on: https://go-review.googlesource.com/c/go/+/255858 Trust: Cherry Zhang <cherryyz@google.com> Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Jeremy Faller <jeremy@golang.org>
2020-08-17cmd/link: avoid duplicate DT_NEEDED entriesJoel Sing
When adding a new library entry, ensure we record it as seen to avoid adding duplicates of it. Fixes #39256 Change-Id: Id309adf80c533d78fd485517c18bc9ab5f1d29fb Reviewed-on: https://go-review.googlesource.com/c/go/+/235257 Run-TryBot: Joel Sing <joel@sing.id.au> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
2020-06-24[dev.link] cmd/{compile,link}: fix file/line of last instruction in DWARF ↵Than McIntosh
line table The code in the compiler's DWARF line table generation emits line table ops at the end of each function fragment to reset the state machine registers back to an initial state, so that when the line table fragments for each function are stitched together into a compilation unit, each fragment will have a clean starting point. The set-file/set-line ops emitted in this code were being applied to the last row of the line table, however, meaning that they were overwriting the existing values. To avoid this problem, add code to advance the PC past the end of the last instruction in the function, and switch to just using an end-of-sequence operator at the end of each function instead of explicit set-file/set-line ops. Updates #39757. Change-Id: Ieb30f83444fa86fb1f2cd53862d8cc8972bb8763 Reviewed-on: https://go-review.googlesource.com/c/go/+/239286 Reviewed-by: Cherry Zhang <cherryyz@google.com> Reviewed-by: Jeremy Faller <jeremy@golang.org> Reviewed-by: David Chase <drchase@google.com>
2020-06-11[dev.link] cmd/compile, cmd/link: remove dead methods if type is not used in ↵Cherry Zhang
interface Currently, a method of a reachable type is live if it matches a method of a reachable interface. In fact, we only need to retain the method if the type is actually converted to an interface. If the type is never converted to an interface, there is no way to call the method through an interface method call (but the type descriptor could still be used, e.g. in calling runtime.newobject). A type can be used in an interface in two ways: - directly converted to interface. (Any interface counts, as it is possible to convert one interface to another.) - obtained by reflection from a related type (e.g. obtaining an interface of T from []T). For the former, we let the compiler emit a marker on the type descriptor symbol when it is converted to an interface. In the linker, we only need to check methods of marked types. For the latter, when the linker visits a marked type, it needs to visit all its "child" types as marked (i.e. potentially could be converted to interface). This reduces binary size: cmd/compile 18792016 18706096 (-0.5%) cmd/go 14120572 13398948 (-5.1%) Change-Id: I4465c7eeabf575f4dc84017214c610fa05ae31fd Reviewed-on: https://go-review.googlesource.com/c/go/+/237298 Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Jeremy Faller <jeremy@golang.org>
2020-06-03cmd/link: new DWARF line table test caseThan McIntosh
Add a test case for an issue with how Go emits DWARF line tables, specifically relating to the line table "end sequence" operator. Updates #38192. Change-Id: I878b262e6ca6c550c0e460c3d5a1969ac4a2c31b Reviewed-on: https://go-review.googlesource.com/c/go/+/235917 Run-TryBot: Than McIntosh <thanm@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com>
2020-05-01cmd/link: don't mark a symbol's Gotype reachableCherry Zhang
A symbol being reachable doesn't imply its type descriptor is needed. Don't mark it. If the type is converted to interface somewhere in the program, there will be an explicit use of the type descriptor, which will make it marked. A println("hello") program before and after -rwxr-xr-x 1 cherryyz primarygroup 1259824 Apr 30 23:00 hello -rwxr-xr-x 1 cherryyz primarygroup 1169680 Apr 30 23:10 hello Updates #38782. Updates #6853. Change-Id: I88884c126ce75ba073f1ba059c4b892c87d2ac96 Reviewed-on: https://go-review.googlesource.com/c/go/+/231397 Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Alessandro Arzilli <alessandro.arzilli@gmail.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2019-05-31cmd/link: revise test case to work on pre-10.14 macosThan McIntosh
Rework this recently introduced test case to insure that it works with older versions of the OS. It was using a new framework library not available on pre-10.14 to trigger the weak symbol reference; switch to using a new symbol from an existing library. Tested on MacOS 10.14 and 10.11. Updates #32233. Change-Id: I1fe2a9255fca46cb7cdf33ff7fed67bba86fdc22 Reviewed-on: https://go-review.googlesource.com/c/go/+/179837 Run-TryBot: Than McIntosh <thanm@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com>
2019-05-30cmd/link: new test case for Darwin/DWARFThan McIntosh
Test case for issue 32233. Updates #32233. Change-Id: I0e3b4a46832f39de4ef36d8fd8c6070bf9b1a019 Reviewed-on: https://go-review.googlesource.com/c/go/+/178726 Reviewed-by: Cherry Zhang <cherryyz@google.com> Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
2019-02-20cmd/link/internal/ld: make dwarf_test and associated testdata module-agnosticBryan C. Mills
Updates #30228 Change-Id: I31aac4cb113c0c88a54329181ad27aee3d8acc71 Reviewed-on: https://go-review.googlesource.com/c/162835 Run-TryBot: Bryan C. Mills <bcmills@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Than McIntosh <thanm@google.com>
2018-07-10cmd/compile: call objabi.PathToPrefix when emitting abstract fnThan McIntosh
When generating an abstract function DIE, call objabi.PathToPrefix on the import path so as to be consistent with how the linker handles import paths. This is intended to resolve another problem with DWARF inline info generation in which there are multiple inconsistent versions of an abstract function DIE for a function whose package path is rewritten/canonicalized by objabi.PathToPrefix. Fixes #26237 Change-Id: I4b64c090ae43a1ad87f47587a1a71f19bc5fc8e8 Reviewed-on: https://go-review.googlesource.com/123036 Run-TryBot: Than McIntosh <thanm@google.com> Reviewed-by: Cherry Zhang <cherryyz@google.com> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
2018-06-05cmd/link: fix duplicated "undefined reloc" errorsisharipo
For given program with 2 undefined relocations (main and undefined): package main func undefined() func defined() int { undefined() undefined() return 0 } var x = defined() "go tool link" produces these errors: main.defined: relocation target main.undefined not defined main.defined: relocation target main.undefined not defined runtime.main_main·f: relocation target main.main not defined main.defined: undefined: "main.undefined" main.defined: undefined: "main.undefined" runtime.main_main·f: undefined: "main.main" After this CL is applied: main.defined: relocation target main.undefined not defined runtime.main_main·f: function main is undeclared in the main package Fixes #10978 Improved error message for main proposed in #24809. Change-Id: I4ba8547b1e143bbebeb4d6e29ea05d932124f037 Reviewed-on: https://go-review.googlesource.com/113955 Run-TryBot: Iskander Sharipov <iskander.sharipov@intel.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-05-25cmd/compile: fix DWARF inline debug issue with dead local varsThan McIntosh
Fix a problem in DWARF inline debug generation relating to handling of statically unreachable local variables. For a function such as: const always = true func HasDeadLocal() int { if always { return 9 } x := new(Something) ... return x.y } the variable "x" is placed onto the Dcl list for the function during parsing, but the actual declaration node is deleted later on when gc.Main invokes "deadcode". Later in the compile the DWARF code emits an abstract function with "x" (since "x" was on the Dcl list at the point of the inline), but the export data emitted does not contain "x". This then creates clashing/inconsistant DWARF abstract function DIEs later on if HasDeadLocal is inlined in somewhere else. As a fix, the inliner now pruned away variables such as "x" when creating a copy of the Dcl list as part of the inlining; this means that both the export data generator and the DWARF emitter wind up seeing a consistent picture. Fixes #25459 Change-Id: I753dc4e9f9ec694340adba5f43c907ba8cc9badc Reviewed-on: https://go-review.googlesource.com/114090 Run-TryBot: Than McIntosh <thanm@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>