From patchwork Mon Nov 30 01:53:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1407929 X-Patchwork-Delegate: sjg@chromium.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.a=rsa-sha256 header.s=google header.b=V3VioO+G; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4CkpGF0RLFz9sTc for ; Mon, 30 Nov 2020 12:55:17 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id B5B14827B4; Mon, 30 Nov 2020 02:54:55 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="V3VioO+G"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 3A15182786; Mon, 30 Nov 2020 02:54:46 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-3.5 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-il1-x143.google.com (mail-il1-x143.google.com [IPv6:2607:f8b0:4864:20::143]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 00D4682607 for ; Mon, 30 Nov 2020 02:54:36 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=sjg@chromium.org Received: by mail-il1-x143.google.com with SMTP id f5so9801487ilj.9 for ; Sun, 29 Nov 2020 17:54:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=YyjC/O8+rxnwcuVWqZ1XWOT0qV4WZ+7pWd59trey9II=; b=V3VioO+GeGoog9ld0FTLMduEjUpX62uCgzk8BRQuEc4nruM2wtlYxfpCjFzz7J6ZJJ IzeOuZ/dyfvwjUaPOfQpeoHitIv56JkmDFZcaSzN6yje18h0+/7BYxnfNrx5h/Rqz0HA qm6vPsvkcWSXzUDsTxqh9CiUuPZhuHTNie6Ko= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=YyjC/O8+rxnwcuVWqZ1XWOT0qV4WZ+7pWd59trey9II=; b=klhhGziUaxEbd5izZ+rRuU2tIa+D3kMfS8KMG4t09+FGGxwvbZpzmaW9lU3Fxtq0lt MdjRl5F5I1Oc22M/4v8xjpRl9ZzEJaQOKvvbJYQeLKs9yAjd2rRKahNwTvgHUJa+FR2A zZJa1HQVtfYN86Pb5nnG/5CDHicOVVqKrwzhQlSeZvIV47c8/PqJvnF4dW28txqOV+Cl I+l20lNRhyrJpe6wZAG0Gq4HGCuapTMEwobAChbfSLHzq/5CcH8aaLgeekOqKCJle3// 9MLMfM2ozwDBaLnuxa5aT041Z5Zn/ouuemqjac7cvk/jSYB4X5mrxNdmjlvAH/+r9yD9 j8UQ== X-Gm-Message-State: AOAM531N5SXVCl0MpL/oKLAxxi3JD5U0uLRg1eCVc0FAYKZutxtxYdxR Yl1QQ6Q91PR6T0ftx1+nbFB/kauI/lzc3A== X-Google-Smtp-Source: ABdhPJyIYb88KM9QYooPLUmeeXcz7emTl+iuLhbFJKSLerokY9ncMiNy418Utxi4se4DVQJt4aFXPA== X-Received: by 2002:a05:6e02:1948:: with SMTP id x8mr11216307ilu.225.1606701274622; Sun, 29 Nov 2020 17:54:34 -0800 (PST) Received: from localhost.localdomain (c-67-190-101-114.hsd1.co.comcast.net. [67.190.101.114]) by smtp.gmail.com with ESMTPSA id q5sm6543341ilg.62.2020.11.29.17.54.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 29 Nov 2020 17:54:34 -0800 (PST) From: Simon Glass To: U-Boot Mailing List Cc: Simon Glass , Bin Meng , Heinrich Schuchardt , Michael Walle , Michal Simek , Ovidiu Panait Subject: [PATCH 01/27] linker_lists: Fix alignment issue Date: Sun, 29 Nov 2020 18:53:36 -0700 Message-Id: <20201129185331.1.Ie3fa227cbe539fa6742fa1e647093557a2b9b8ee@changeid> X-Mailer: git-send-email 2.29.2.454.gaff20da3a2-goog In-Reply-To: <20201130015402.2328621-1-sjg@chromium.org> References: <20201130015402.2328621-1-sjg@chromium.org> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.3 at phobos.denx.de X-Virus-Status: Clean The linker script uses alphabetic sorting to group the different linker lists together. Each group has its own struct and potentially its own alignment. But when the linker packs the structs together it cannot ensure that a linker list starts on the expected alignment boundary. For example, if the first list has a struct size of 8 and we place 3 of them in the image, that means that the next struct will start at offset 0x18 from the start of the linker_list section. If the next struct has a size of 16 then it will start at an 8-byte aligned offset, but not a 16-byte aligned offset. With sandbox on x86_64, a reference to a linker list item using ll_entry_get() can force alignment of that particular linker_list item, if it is in the same file as the linker_list item is declared. Consider this example, where struct driver is 0x80 bytes: ll_entry_declare(struct driver, fred, driver) ... void *p = ll_entry_get(struct driver, fred, driver) If these two lines of code are in the same file, then the entry is forced to be aligned at the 'struct driver' alignment, which is 16 bytes. If the second line of code is in a different file, then no action is taken, since the compiler cannot update the alignment of the linker_list item. In the first case, an 8-byte 'fill' region is added: .u_boot_list_2_driver_2_testbus_drv 0x0000000000270018 0x80 test/built-in.o 0x0000000000270018 _u_boot_list_2_driver_2_testbus_drv .u_boot_list_2_driver_2_testfdt1_drv 0x0000000000270098 0x80 test/built-in.o 0x0000000000270098 _u_boot_list_2_driver_2_testfdt1_drv *fill* 0x0000000000270118 0x8 .u_boot_list_2_driver_2_testfdt_drv 0x0000000000270120 0x80 test/built-in.o 0x0000000000270120 _u_boot_list_2_driver_2_testfdt_drv .u_boot_list_2_driver_2_testprobe_drv 0x00000000002701a0 0x80 test/built-in.o 0x00000000002701a0 _u_boot_list_2_driver_2_testprobe_drv With this, the linker_list no-longer works since items after testfdt1_drv are not at the expected address. Ideally we would have a way to tell gcc not to align structs in this way. It is not clear how we could do this, and in any case it would require us to adjust every struct used by the linker_list feature. One possible fix is to force each separate linker_list to start on the largest possible boundary that can be required by the compiler. However that does not seem to work on x86_64, which uses 16-byte alignment in this case but needs 32-byte alignment. So add a Kconfig option to handle this. Set the default value to 4 so as to avoid changing platforms that don't need it. Update the ll_entry_start() accordingly. Signed-off-by: Simon Glass --- arch/Kconfig | 11 +++++++ doc/api/linker_lists.rst | 62 ++++++++++++++++++++++++++++++++++++++++ include/linker_lists.h | 3 +- 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/arch/Kconfig b/arch/Kconfig index 3aa99e08fce..aa8664212f1 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -7,6 +7,17 @@ config HAVE_ARCH_IOREMAP config NEEDS_MANUAL_RELOC bool +config LINKER_LIST_ALIGN + int + default 32 if SANDBOX + default 8 if ARM64 || X86 + default 4 + help + Force the each linker list to be aligned to this boundary. This + is required if ll_entry_get() is used, since otherwise the linker + may add padding into the table, thus breaking it. + See linker_lists.rst for full details. + choice prompt "Architecture select" default SANDBOX diff --git a/doc/api/linker_lists.rst b/doc/api/linker_lists.rst index 72f514e0ac0..7a37db52ba8 100644 --- a/doc/api/linker_lists.rst +++ b/doc/api/linker_lists.rst @@ -96,5 +96,67 @@ defined for the whole list and each sub-list: %u_boot_list_2_drivers_2_pci_3 %u_boot_list_2_drivers_3 +Alignment issues +---------------- + +The linker script uses alphabetic sorting to group the different linker +lists together. Each group has its own struct and potentially its own +alignment. But when the linker packs the structs together it cannot ensure +that a linker list starts on the expected alignment boundary. + +For example, if the first list has a struct size of 8 and we place 3 of +them in the image, that means that the next struct will start at offset +0x18 from the start of the linker_list section. If the next struct has +a size of 16 then it will start at an 8-byte aligned offset, but not a +16-byte aligned offset. + +With sandbox on x86_64, a reference to a linker list item using +ll_entry_get() can force alignment of that particular linker_list item, +if it is in the same file as the linker_list item is declared. + +Consider this example, where struct driver is 0x80 bytes: + +:: + + ll_entry_declare(struct driver, fred, driver) + + ... + + void *p = ll_entry_get(struct driver, fred, driver) + +If these two lines of code are in the same file, then the entry is forced +to be aligned at the 'struct driver' alignment, which is 16 bytes. If the +second line of code is in a different file, then no action is taken, since +the compiler cannot update the alignment of the linker_list item. + +In the first case, an 8-byte 'fill' region is added: + +:: + + .u_boot_list_2_driver_2_testbus_drv + 0x0000000000270018 0x80 test/built-in.o + 0x0000000000270018 _u_boot_list_2_driver_2_testbus_drv + .u_boot_list_2_driver_2_testfdt1_drv + 0x0000000000270098 0x80 test/built-in.o + 0x0000000000270098 _u_boot_list_2_driver_2_testfdt1_drv + *fill* 0x0000000000270118 0x8 + .u_boot_list_2_driver_2_testfdt_drv + 0x0000000000270120 0x80 test/built-in.o + 0x0000000000270120 _u_boot_list_2_driver_2_testfdt_drv + .u_boot_list_2_driver_2_testprobe_drv + 0x00000000002701a0 0x80 test/built-in.o + 0x00000000002701a0 _u_boot_list_2_driver_2_testprobe_drv + +With this, the linker_list no-longer works since items after testfdt1_drv +are not at the expected address. + +Ideally we would have a way to tell gcc not to align structs in this way. +It is not clear how we could do this, and in any case it would require us +to adjust every struct used by the linker_list feature. + +The simplest fix seems to be to force each separate linker_list to start +on the largest possible boundary that can be required by the compiler. This +is the purpose of CONFIG_LINKER_LIST_ALIGN + .. kernel-doc:: include/linker_lists.h :internal: diff --git a/include/linker_lists.h b/include/linker_lists.h index d775d041e04..fd98ecd297c 100644 --- a/include/linker_lists.h +++ b/include/linker_lists.h @@ -124,7 +124,8 @@ */ #define ll_entry_start(_type, _list) \ ({ \ - static char start[0] __aligned(4) __attribute__((unused, \ + static char start[0] __aligned(CONFIG_LINKER_LIST_ALIGN) \ + __attribute__((unused, \ section(".u_boot_list_2_"#_list"_1"))); \ (_type *)&start; \ })