| Message ID | 20260814085043.2824706-1-torbjorn.svensson@foss.st.com |
|---|---|
| State | New |
| Headers |
Return-Path: <newlib-bounces~patchwork=sourceware.org@sourceware.org> X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D9C654BA9015 for <patchwork@sourceware.org>; Fri, 14 Aug 2026 08:51:25 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D9C654BA9015 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=foss.st.com header.i=@foss.st.com header.a=rsa-sha256 header.s=selector2 header.b=ZqzUxbcA X-Original-To: newlib@sourceware.org Delivered-To: newlib@sourceware.org Received: from MRWPR03CU001.outbound.protection.outlook.com (mail-francesouthazon11011068.outbound.protection.outlook.com [40.107.130.68]) by sourceware.org (Postfix) with ESMTPS id E304D4BA2E06 for <newlib@sourceware.org>; Fri, 14 Aug 2026 08:51:06 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E304D4BA2E06 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=foss.st.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=foss.st.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org E304D4BA2E06 Authentication-Results: sourceware.org; arc=pass smtp.remote-ip=40.107.130.68 ARC-Seal: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1786697467; cv=pass; b=xtG76u667KDJNgQtcJhXIO1TFqMdICv+rbijl009tNHDtYFjKqICjm7WIkhidy2Pn3ww7+zC/vg0s2b59U4Kgq5KOuJCBvFLSDDYgYiqorjPKjzcB3P/vs7nOlzQH5XoB+Il7CYOjlnvQ3i5th2J0q+Y1T+9e/07/K1DbA0b5Mw= ARC-Message-Signature: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1786697467; c=relaxed/simple; bh=NKzJYTBQsXO+sP8tBNjPWx+pddekKfVFTPloOUorBAk=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=QVc/UhrCA5sm4qg5Bj7fV3ysTyKNaTfYdp1NFKQQ0i+gX+K92DXCK/a8nQI2MPZtJiRHm8o98wOtWALoCxVoP4lOlQQF/p/GmPcwAt/AnozbW40M1QnXyGQOr+lEki/0kVpH+7Pot5ZgpPMlxnmw1O8KM6hqERJmBlqeoAlLFmI= ARC-Authentication-Results: i=2; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=foss.st.com header.i=@foss.st.com header.a=rsa-sha256 header.s=selector2 header.b=ZqzUxbcA DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E304D4BA2E06 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=eSpgNrlUPUkkA4pz/x13iG5LOBsorNZyAO/qiXZRE2ie0FHh4MoFQz2xUinlwUo8JEWfy5CoXNw98kobJqZwW9JpcP9VWvsoYy5EaksSlklSVT9SlNdTPbJ1Ib7NC/63/O5qYp/ni/LetpkFy48zLDKb3RHnwlC/c5HZmAfL7phAqgcI5AxipwZlcBGcKHcpPRkJjw/aKvkSEThNd4spxwWQx0FoLPAlzyQ5LiFC23yA9pjJrsk2spWkkH5oAE1KveyxfGDp8ni/PfgJ5rN2vExM+g8Id9mMZa8ztBuEpY6q7kJXcR5uDPN9bwRL0JRJ5CjHH4yvR1vlJpIkNZHJVw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=f/5SRtj/5/WZ2k6hBRH4+6FLKO83mHAgNxTxpYfBtYA=; b=xjmKAD+QWb2azWRPQiJ55kRbnHqIXISDOYymRbozKbgi9KJSyp0zvWjge1muaii/0qHZI2pP9kiWjDBD4TVHQRclLcxbJpp4q3icVNCBMjUHMcU+PyIFCxhd6uC3MAt1iT5zwIV4hCzMAcY2dgKhnwVhXn02jfJmTEWGVACyA6JSaJc4FZ+Rs2OWvOaEZLaLNQtNUcRUSVhEnrFKWjocKXQsagk6o+CFiW3uiuB5KQt5Mh1oi03jK+322PNRXQIBusFs3VpPtxdQKQl10iyqjlkK1Dede1oYdvUKtp/fkPQlbYiCIiGJgy5LI8ZZj/eBNKTgWMLcE0xtGItNl4FUjg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=fail (sender ip is 164.130.1.59) smtp.rcpttodomain=sourceware.org smtp.mailfrom=foss.st.com; dmarc=fail (p=none sp=none pct=100) action=none header.from=foss.st.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=foss.st.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=f/5SRtj/5/WZ2k6hBRH4+6FLKO83mHAgNxTxpYfBtYA=; b=ZqzUxbcAHfqseN0fNz5+Dc2Wc7lrGk5MofjTnUwPhIUvKK8w8guwAYTn8xh6ItdUQLlp80/8RA7qOzSRlRc0bwM4OlVJCQiqVzcAAUnNir3uenozIBIQYRAcPEdEng23OKQ3C6P8tRtunbKElY5JYu4TtGNgwQ0B++ysrpSOO1xQI/Xbr2IxgRnk7cCI8RZFBKwmLVpJnxfEIww2iRNuoY3+lGfV3rWk+eoMIAoHzAemQsVm9ypgFzJX4KZhq0uQVX1haxgMJzuwVupOXGEfQG4bFziDh+QUVGs2yy+tmiOCXXuBPym2eeYKM9aJn2LvDe98QkvnU9FN2oRk6wMMlw== Received: from DB9PR02CA0028.eurprd02.prod.outlook.com (2603:10a6:10:1d9::33) by DB9PR10MB8029.EURPRD10.PROD.OUTLOOK.COM (2603:10a6:10:3d7::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.315.16; Fri, 14 Aug 2026 08:50:58 +0000 Received: from DB1PEPF000509EE.eurprd03.prod.outlook.com (2603:10a6:10:1d9:cafe::34) by DB9PR02CA0028.outlook.office365.com (2603:10a6:10:1d9::33) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.21.315.15 via Frontend Transport; Fri, 14 Aug 2026 08:50:58 +0000 X-MS-Exchange-Authentication-Results: spf=fail (sender IP is 164.130.1.59) smtp.mailfrom=foss.st.com; dkim=none (message not signed) header.d=none;dmarc=fail action=none header.from=foss.st.com; Received-SPF: Fail (protection.outlook.com: domain of foss.st.com does not designate 164.130.1.59 as permitted sender) receiver=protection.outlook.com; client-ip=164.130.1.59; helo=smtpO365.st.com; Received: from smtpO365.st.com (164.130.1.59) by DB1PEPF000509EE.mail.protection.outlook.com (10.167.242.72) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.315.6 via Frontend Transport; Fri, 14 Aug 2026 08:50:57 +0000 Received: from STKDAG1NODE2.st.com (10.75.128.133) by smtpo365.st.com (10.250.44.71) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.43; Fri, 14 Aug 2026 10:56:27 +0200 Received: from jkgcxl0012.jkg.st.com (10.74.22.122) by STKDAG1NODE2.st.com (10.75.128.133) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.43; Fri, 14 Aug 2026 10:50:55 +0200 From: =?utf-8?q?Torbj=C3=B6rn_SVENSSON?= <torbjorn.svensson@foss.st.com> To: <newlib@sourceware.org> CC: <richard.earnshaw@arm.com>, <christophe.lyon@arm.com>, <tkaminsk@redhat.com>, <schwab@suse.de>, <jwakely.gcc@gmail.com>, =?utf-8?q?Torbj=C3=B6rn_SVENSSON?= <torbjorn.svensson@foss.st.com> Subject: [PATCH] libgloss/arm: report semihosted directories correctly Date: Fri, 14 Aug 2026 10:09:51 +0200 Message-ID: <20260814085043.2824706-1-torbjorn.svensson@foss.st.com> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit X-Originating-IP: [10.74.22.122] X-ClientProxiedBy: STKCAS1NODE1.st.com (10.75.128.134) To STKDAG1NODE2.st.com (10.75.128.133) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: DB1PEPF000509EE:EE_|DB9PR10MB8029:EE_ X-MS-Office365-Filtering-Correlation-Id: 5fde6fa6-d051-4116-09e8-08def9e128d2 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|1800799024|23010399003|376014|42112799006|82310400026|36860700016|10067099003|56012099006|6133799003|11063799006|18002099003|13003099007; X-Microsoft-Antispam-Message-Info: Ou+QqnfLozrgbex20gHxK+f0to96jp3wLkBgBhRycsFbHz2LVKqWC0lLQ9//lXLV2RGYDZHYC4QnKzfTd/iWEdjWu95/niyEqukzyLv2tqPSfn8UIBifBRYZNa7TA6mekNg/fBoxAg6zMfTSTfhYOM9dTrxeyWv8tPa8QUDfQToXm2WTyR5RTXM+9A8qcvicG9Zdqdunp1yTmks78PbfE2Q1OR33DCjjZlKK3s8p8QLVKPwh7PvwDWjrD17cWrivkNhg6Udvi/e6Kt42qO3kqyAyl2QxN8VVUam+XfmHJRzSueua18QaFIBeIpLAsscyt/d5q1nNG/5fChO+EsX3Zjl06irtDQwphazyREN7uAO9p8C3E/GQ/wl5xXcCQdZ3GIi/IacPRTsF5ANPMBOQY9f6dcnwPoKSHmbej/7jowb9rhyK7OVLvb3ZqvxcSLgtHcGZkMxR0JvRFOAyoKmR1z5hrNhE5xb57AE6wDmnDb4I2C3SGo6zLkoejx46X6kRjiIThOiCIl9peDl3K6TmaGVApLqRePE+mLf81nsnZ4axHEx0wDEBdwHPPcu83iHNGAVUxJRvcghGhdaInJ5Tq6mI+xafriyCOSeZiZNZlU0ytJsllKzXT+IKDGOAzaAbfXJiVtDZr3R+Arli5EujuBQteeXzjo9W0Cff3pP2rSmxqQi/N4ClvOTYMUAYBWw0Tc6i2fyZyQ5/D3QBqyzrvQ== X-Forefront-Antispam-Report: CIP:164.130.1.59; CTRY:IT; LANG:en; SCL:1; SRV:; IPV:CAL; SFV:NSPM; H:smtpO365.st.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230040)(1800799024)(23010399003)(376014)(42112799006)(82310400026)(36860700016)(10067099003)(56012099006)(6133799003)(11063799006)(18002099003)(13003099007); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: JKnwGfCnkyaoBQxpb9AsVyIC5MZ/8ShbiPlgMD3mkhs9E/nN+glcALWX4so0qt/w44HWtaTkE/vhZHWhy59/E+xlZxf47h/Mcjxrkr4/vSAFBZxDQ0D3fFyXZBVFh0BxODra5rwbKYhI30AHHBL8ZnTnT4qR5WTp+7RrqXlVOeuPolOqOt4ClM0OKTelbB+SdOG9WQQuriSwN21g9vapgU87iJKdbdRmmLetyxZzjyxsQ6D5ZeaekHUAtoyhcR5MGy6h3h+H+FyMWQFraLRSELEcpfIMeN0We0CXzMr8lcgen3DKz7BDEwjK2a6W92PdaeJLgSiNgsfrzctDpOxnxeE3ErziIViAEGctFRP9nnIEloGa+ryuLARrcdLdl5YwwPVNEz2OjKQ+zSXI23dMbXImQBU8ReZCkITfgpBMDmudP0DocJkFEbaD9xA/tZOR X-OriginatorOrg: foss.st.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 14 Aug 2026 08:50:57.9740 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 5fde6fa6-d051-4116-09e8-08def9e128d2 X-MS-Exchange-CrossTenant-Id: 75e027c9-20d5-47d5-b82f-77d7cd041e8f X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=75e027c9-20d5-47d5-b82f-77d7cd041e8f; Ip=[164.130.1.59]; Helo=[smtpO365.st.com] X-MS-Exchange-CrossTenant-AuthSource: DB1PEPF000509EE.eurprd03.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB9PR10MB8029 X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_PASS, SPF_PASS, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: newlib@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Newlib mailing list <newlib.sourceware.org> List-Unsubscribe: <https://sourceware.org/mailman/options/newlib>, <mailto:newlib-request@sourceware.org?subject=unsubscribe> List-Archive: <https://sourceware.org/pipermail/newlib/> List-Post: <mailto:newlib@sourceware.org> List-Help: <mailto:newlib-request@sourceware.org?subject=help> List-Subscribe: <https://sourceware.org/mailman/listinfo/newlib>, <mailto:newlib-request@sourceware.org?subject=subscribe> Errors-To: newlib-bounces~patchwork=sourceware.org@sourceware.org |
| Series |
libgloss/arm: report semihosted directories correctly
|
|
Commit Message
Torbjorn SVENSSON
Aug. 14, 2026, 8:09 a.m. UTC
In GCC r17-2048-gcc195f7b11a406, support for resolving /etc/localtime symlinks
was added. As a consequense, when using semihosting for arm-none-eabi, the
resolution fails due to that semihosting unconditionally consider all paths as
character devices. This commit tries to work around that by inferring as much
stat details as possible from available APIs in the semihosting specification.
With this change, I see that the failure is resolved, but there are a few
tests that have been marked as xfail that now passes. I will follow up
with a patch for GCC as soon as this change is merged.
These are the changes when testing with GCC r17-2833-g32657f29f91871.
Without patch:
PASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 (test for excess errors)
XFAIL: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 execution test
PASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 (test for excess errors)
XFAIL: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 execution test
PASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 (test for excess errors)
XFAIL: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 execution test
PASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 (test for excess errors)
XFAIL: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 execution test
PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 (test for excess errors)
FAIL: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 execution test
PASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 (test for excess errors)
XFAIL: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 execution test
PASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 (test for excess errors)
XFAIL: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 execution test
PASS: std/time/tzdb/1.cc -std=gnu++20 (test for excess errors)
FAIL: std/time/tzdb/1.cc -std=gnu++20 execution test
PASS: std/time/zoned_time/custom.cc -std=gnu++20 (test for excess errors)
FAIL: std/time/zoned_time/custom.cc -std=gnu++20 execution test
With patch:
PASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 (test for excess errors)
XPASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 execution test
PASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 (test for excess errors)
XPASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 execution test
PASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 (test for excess errors)
XPASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 execution test
PASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 (test for excess errors)
XPASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 execution test
PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 (test for excess errors)
PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 execution test
PASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 (test for excess errors)
XPASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 execution test
PASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 (test for excess errors)
XPASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 execution test
PASS: std/time/tzdb/1.cc -std=gnu++20 (test for excess errors)
PASS: std/time/tzdb/1.cc -std=gnu++20 execution test
PASS: std/time/zoned_time/custom.cc -std=gnu++20 (test for excess errors)
PASS: std/time/zoned_time/custom.cc -std=gnu++20 execution test
These is also an implementation choise to be made.
Would it be prefered to use stack (like I do in this patch) or would it be
better to allocated the temporary string in path_is_dir on the heap using
malloc? Regardless of solution, semihosting is expensive, so I don't think
it really matters what solution is used. I choose stack based for the first
version as it does not pull in any extra functions.
Ok to push to master as-is or should I change to a heap based implementation?
Kind regards,
Torbjörn
--
Arm semihosting does not provide a real stat operation, so _stat has to
infer file information from the operations that are available.
The previous code always treated an opened path as a regular file, and
_swistat then added S_IFCHR unconditionally. That meant directories were
not reported as directories, and the file type bits could end up
describing more than one kind of file at once.
Use _isatty to identify character devices, use SYS_FLEN for regular
files, and probe "path/." so _stat can recognize directories.
This gives callers such as std::filesystem::canonical enough information
to handle semihosted paths correctly.
Discovered, and reported, in
https://gcc.gnu.org/pipermail/gcc-patches/2026-August/727185.html
Signed-off-by: Torbjörn SVENSSON <torbjorn.svensson@foss.st.com>
---
libgloss/arm/syscalls.c | 79 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 74 insertions(+), 5 deletions(-)
Comments
On Fri, 14 Aug 2026, 09:51 Torbjörn SVENSSON, <torbjorn.svensson@foss.st.com> wrote: > In GCC r17-2048-gcc195f7b11a406, support for resolving /etc/localtime > symlinks > was added. As a consequense, when using semihosting for arm-none-eabi, the > resolution fails due to that semihosting unconditionally consider all > paths as > character devices. This commit tries to work around that by inferring as > much > stat details as possible from available APIs in the semihosting > specification. > > With this change, I see that the failure is resolved, but there are a few > tests that have been marked as xfail that now passes. I will follow up > with a patch for GCC as soon as this change is merged. > > These are the changes when testing with GCC r17-2833-g32657f29f91871. > > Without patch: > PASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 (test for > excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 (test for > excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 (test for > excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 (test for > excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 (test > for excess errors) > FAIL: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 > execution test > PASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 (test for > excess errors) > XFAIL: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 execution > test > PASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 (test > for excess errors) > XFAIL: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 > execution test > PASS: std/time/tzdb/1.cc -std=gnu++20 (test for excess errors) > FAIL: std/time/tzdb/1.cc -std=gnu++20 execution test > PASS: std/time/zoned_time/custom.cc -std=gnu++20 (test for excess errors) > FAIL: std/time/zoned_time/custom.cc -std=gnu++20 execution test > > With patch: > PASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 (test for > excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 (test for > excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 (test for > excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 (test for > excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 (test > for excess errors) > PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 > execution test > PASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 (test for > excess errors) > XPASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 execution > test > PASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 (test > for excess errors) > XPASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 > execution test > PASS: std/time/tzdb/1.cc -std=gnu++20 (test for excess errors) > PASS: std/time/tzdb/1.cc -std=gnu++20 execution test > PASS: std/time/zoned_time/custom.cc -std=gnu++20 (test for excess errors) > PASS: std/time/zoned_time/custom.cc -std=gnu++20 execution test > > > These is also an implementation choise to be made. > Would it be prefered to use stack (like I do in this patch) or would it be > better to allocated the temporary string in path_is_dir on the heap using > malloc? Regardless of solution, semihosting is expensive, so I don't think > it really matters what solution is used. I choose stack based for the first > version as it does not pull in any extra functions. > > Ok to push to master as-is or should I change to a heap based > implementation? > > Kind regards, > Torbjörn > > -- > > Arm semihosting does not provide a real stat operation, so _stat has to > infer file information from the operations that are available. > > The previous code always treated an opened path as a regular file, and > _swistat then added S_IFCHR unconditionally. That meant directories were > not reported as directories, and the file type bits could end up > describing more than one kind of file at once. > > Use _isatty to identify character devices, use SYS_FLEN for regular > files, and probe "path/." so _stat can recognize directories. > > This gives callers such as std::filesystem::canonical enough information > to handle semihosted paths correctly. > > Discovered, and reported, in > https://gcc.gnu.org/pipermail/gcc-patches/2026-August/727185.html > > Signed-off-by: Torbjörn SVENSSON <torbjorn.svensson@foss.st.com> > --- > libgloss/arm/syscalls.c | 79 ++++++++++++++++++++++++++++++++++++++--- > 1 file changed, 74 insertions(+), 5 deletions(-) > > diff --git a/libgloss/arm/syscalls.c b/libgloss/arm/syscalls.c > index 710a741ee..162059fc0 100644 > --- a/libgloss/arm/syscalls.c > +++ b/libgloss/arm/syscalls.c > @@ -15,6 +15,7 @@ > #include <reent.h> > #include <unistd.h> > #include <sys/wait.h> > +#include <limits.h> > #include "swi.h" > > /* Forward prototypes. */ > @@ -46,6 +47,7 @@ void initialise_monitor_handles (void); > static int checkerror (int); > static int error (int); > static int get_errno (void); > +static int path_is_dir (const char *); > > /* Semihosting utilities. */ > static void initialise_semihosting_exts (void); > @@ -346,6 +348,46 @@ checkerror (int result) > return result; > } > > +/* Check if the given path is likely a directory. */ > +static int > +path_is_dir (const char *path) > +{ > + size_t path_len; > + size_t dir_len; > + char dir_path[PATH_MAX + 3]; > + int fd; > + > + path_len = strlen (path); > + if (path_len == 0) > + return 0; > + > + if (path_len + 3 > sizeof (dir_path)) > + { > + errno = ENAMETOOLONG; > + return -1; > + } > + > + /* Build a new string that ends with "/.". */ > + memcpy (dir_path, path, path_len); > + dir_len = path_len; > + if (dir_path[dir_len - 1] != '/') > Just an observation... This check means you will add /. to a path ending with /.. which is redundant, but I think that's OK. If strlen(dir_path) == sizeof(dir_path) and it ends with /.. then there's no need to append anything, but this function will return ENAMETOOLONG. Again, I think that's OK. It's an edge case and the path is already longer than PATH_MAX. + dir_path[dir_len++] = '/'; > + dir_path[dir_len++] = '.'; > + dir_path[dir_len] = '\0'; > + > + /* Try to open the directory. */ > + fd = _open (dir_path, O_RDONLY); > + > + /* Error means that path either does not exist > + * or is not a directory. */ > + if (fd == -1) > + return 0; > + > + /* Not interested in the error. */ > + _close (fd); > + return 1; > +} > + > /* fh, is a valid internal file handle. > ptr, is a null terminated string. > len, is the length in bytes to read. > @@ -738,10 +780,14 @@ _swistat (int fd, struct stat * st) > return -1; > } > > - /* Always assume a character device, > - with 1024 byte blocks. */ > - st->st_mode |= S_IFCHR; > st->st_blksize = 1024; > + if (_isatty (fd)) > + { > + /* Assume character device. */ > + st->st_mode |= S_IFCHR; > + return 0; > + } > + > #ifdef ARM_RDI_MONITOR > res = checkerror (do_AngelSWI (AngelSWI_Reason_FLen, &pfd->handle)); > #else > @@ -752,7 +798,19 @@ _swistat (int fd, struct stat * st) > checkerror (res); > #endif > if (res == -1) > - return -1; > + { > + if (errno == EISDIR) > + { > + /* Path is a directory. */ > + st->st_mode |= S_IFDIR; > + return 0; > + } > + return -1; > + } > + > + /* Assume regular file. */ > + st->st_mode |= S_IFREG; > + > /* Return the file size. */ > st->st_size = res; > return 0; > @@ -770,12 +828,23 @@ _stat (const char *fname, struct stat *st) > { > int fd, res; > memset (st, 0, sizeof (* st)); > + res = path_is_dir (fname); > + if (res == 1) > + { > + st->st_mode = S_IFDIR | S_IREAD | S_IEXEC; > + st->st_blksize = 1024; > + return 0; > + } > + else if (res == -1) > + return -1; > + > /* The best we can do is try to open the file readonly. If it exists, > then we can guess a few things about it. */ > if ((fd = _open (fname, O_RDONLY)) == -1) > return -1; > - st->st_mode |= S_IFREG | S_IREAD; > res = _swistat (fd, st); > + if (res == 0) > + st->st_mode |= S_IREAD; > /* Not interested in the error. */ > _close (fd); > return res; > -- > 2.43.0 > >
On 2026-08-14 11:25, Jonathan Wakely wrote: > > > On Fri, 14 Aug 2026, 09:51 Torbjörn SVENSSON, <torbjorn.svensson@foss.st.com <mailto:torbjorn.svensson@foss.st.com>> wrote: > > In GCC r17-2048-gcc195f7b11a406, support for resolving /etc/localtime symlinks > was added. As a consequense, when using semihosting for arm-none-eabi, the > resolution fails due to that semihosting unconditionally consider all paths as > character devices. This commit tries to work around that by inferring as much > stat details as possible from available APIs in the semihosting specification. > > With this change, I see that the failure is resolved, but there are a few > tests that have been marked as xfail that now passes. I will follow up > with a patch for GCC as soon as this change is merged. > > These are the changes when testing with GCC r17-2833-g32657f29f91871. > > Without patch: > PASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 (test for excess errors) > FAIL: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 execution test > PASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 execution test > PASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 execution test > PASS: std/time/tzdb/1.cc -std=gnu++20 (test for excess errors) > FAIL: std/time/tzdb/1.cc -std=gnu++20 execution test > PASS: std/time/zoned_time/custom.cc -std=gnu++20 (test for excess errors) > FAIL: std/time/zoned_time/custom.cc -std=gnu++20 execution test > > With patch: > PASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 (test for excess errors) > PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 execution test > PASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 execution test > PASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 execution test > PASS: std/time/tzdb/1.cc -std=gnu++20 (test for excess errors) > PASS: std/time/tzdb/1.cc -std=gnu++20 execution test > PASS: std/time/zoned_time/custom.cc -std=gnu++20 (test for excess errors) > PASS: std/time/zoned_time/custom.cc -std=gnu++20 execution test > > > These is also an implementation choise to be made. > Would it be prefered to use stack (like I do in this patch) or would it be > better to allocated the temporary string in path_is_dir on the heap using > malloc? Regardless of solution, semihosting is expensive, so I don't think > it really matters what solution is used. I choose stack based for the first > version as it does not pull in any extra functions. > > Ok to push to master as-is or should I change to a heap based implementation? > > Kind regards, > Torbjörn > > -- > > Arm semihosting does not provide a real stat operation, so _stat has to > infer file information from the operations that are available. > > The previous code always treated an opened path as a regular file, and > _swistat then added S_IFCHR unconditionally. That meant directories were > not reported as directories, and the file type bits could end up > describing more than one kind of file at once. > > Use _isatty to identify character devices, use SYS_FLEN for regular > files, and probe "path/." so _stat can recognize directories. > > This gives callers such as std::filesystem::canonical enough information > to handle semihosted paths correctly. > > Discovered, and reported, in > https://gcc.gnu.org/pipermail/gcc-patches/2026-August/727185.html <https://gcc.gnu.org/pipermail/gcc-patches/2026-August/727185.html> > > Signed-off-by: Torbjörn SVENSSON <torbjorn.svensson@foss.st.com <mailto:torbjorn.svensson@foss.st.com>> > --- > libgloss/arm/syscalls.c | 79 ++++++++++++++++++++++++++++++++++++++--- > 1 file changed, 74 insertions(+), 5 deletions(-) > > diff --git a/libgloss/arm/syscalls.c b/libgloss/arm/syscalls.c > index 710a741ee..162059fc0 100644 > --- a/libgloss/arm/syscalls.c > +++ b/libgloss/arm/syscalls.c > @@ -15,6 +15,7 @@ > #include <reent.h> > #include <unistd.h> > #include <sys/wait.h> > +#include <limits.h> > #include "swi.h" > > /* Forward prototypes. */ > @@ -46,6 +47,7 @@ void initialise_monitor_handles (void); > static int checkerror (int); > static int error (int); > static int get_errno (void); > +static int path_is_dir (const char *); > > /* Semihosting utilities. */ > static void initialise_semihosting_exts (void); > @@ -346,6 +348,46 @@ checkerror (int result) > return result; > } > > +/* Check if the given path is likely a directory. */ > +static int > +path_is_dir (const char *path) > +{ > + size_t path_len; > + size_t dir_len; > + char dir_path[PATH_MAX + 3]; > + int fd; > + > + path_len = strlen (path); > + if (path_len == 0) > + return 0; > + > + if (path_len + 3 > sizeof (dir_path)) > + { > + errno = ENAMETOOLONG; > + return -1; > + } > + > + /* Build a new string that ends with "/.". */ > + memcpy (dir_path, path, path_len); > + dir_len = path_len; > + if (dir_path[dir_len - 1] != '/') > > > Just an observation... > > This check means you will add /. to a path ending with /.. which is redundant, but I think that's OK. > > If strlen(dir_path) == sizeof(dir_path) and it ends with /.. then there's no need to append anything, but this function will return ENAMETOOLONG. Again, I think that's OK. It's an edge case and the path is already longer than PATH_MAX. Indeed, this is true. The reason why I did not bother is that it makes the logic a bit more complex and I think the corner case could be dismissed as working anyway. Another thing that hit me with your feedback is what would happen when the PC side of the semihosting is running Windows. Is it okay to mix front- and back-slashes or do we need to normalize the path too? If we normalize to one of them, then should it be front or back? It's also going to be hard to know what is expected on the other side for the semihosting with this code running on an Arm target... I do not consider this as a perfect solution, but I cannot come up with something better that would not require an update of the specification and the software on the PC side. Kind regards, Torbjörn > > > + dir_path[dir_len++] = '/'; > + dir_path[dir_len++] = '.'; > + dir_path[dir_len] = '\0'; > + > + /* Try to open the directory. */ > + fd = _open (dir_path, O_RDONLY); > + > + /* Error means that path either does not exist > + * or is not a directory. */ > + if (fd == -1) > + return 0; > + > + /* Not interested in the error. */ > + _close (fd); > + return 1; > +} > + > /* fh, is a valid internal file handle. > ptr, is a null terminated string. > len, is the length in bytes to read. > @@ -738,10 +780,14 @@ _swistat (int fd, struct stat * st) > return -1; > } > > - /* Always assume a character device, > - with 1024 byte blocks. */ > - st->st_mode |= S_IFCHR; > st->st_blksize = 1024; > + if (_isatty (fd)) > + { > + /* Assume character device. */ > + st->st_mode |= S_IFCHR; > + return 0; > + } > + > #ifdef ARM_RDI_MONITOR > res = checkerror (do_AngelSWI (AngelSWI_Reason_FLen, &pfd->handle)); > #else > @@ -752,7 +798,19 @@ _swistat (int fd, struct stat * st) > checkerror (res); > #endif > if (res == -1) > - return -1; > + { > + if (errno == EISDIR) > + { > + /* Path is a directory. */ > + st->st_mode |= S_IFDIR; > + return 0; > + } > + return -1; > + } > + > + /* Assume regular file. */ > + st->st_mode |= S_IFREG; > + > /* Return the file size. */ > st->st_size = res; > return 0; > @@ -770,12 +828,23 @@ _stat (const char *fname, struct stat *st) > { > int fd, res; > memset (st, 0, sizeof (* st)); > + res = path_is_dir (fname); > + if (res == 1) > + { > + st->st_mode = S_IFDIR | S_IREAD | S_IEXEC; > + st->st_blksize = 1024; > + return 0; > + } > + else if (res == -1) > + return -1; > + > /* The best we can do is try to open the file readonly. If it exists, > then we can guess a few things about it. */ > if ((fd = _open (fname, O_RDONLY)) == -1) > return -1; > - st->st_mode |= S_IFREG | S_IREAD; > res = _swistat (fd, st); > + if (res == 0) > + st->st_mode |= S_IREAD; > /* Not interested in the error. */ > _close (fd); > return res; > -- > 2.43.0 >
On Aug 14 2026, Torbjörn SVENSSON wrote:
> + st->st_mode |= S_IFCHR;
That doesn't make sense. S_IFMT is a multi-value bitfield, not a
colletion of single bits.
On 2026-08-18 10:33, Andreas Schwab wrote: > On Aug 14 2026, Torbjörn SVENSSON wrote: > >> + st->st_mode |= S_IFCHR; > > That doesn't make sense. S_IFMT is a multi-value bitfield, not a > colletion of single bits. > I know that, but what do you want me to do instead? The bitgroup is always cleared at this stage, but I can change it to st->st_mode = (st->st_mode & ~S_IFMT) | S_IFCHR; if that makes more sense (obviously the same for all the other assignments in the file). Let me know what you think. Kind regards, Torbjörn
Gentle ping! :) Kind regards, Torbjörn On 2026-08-14 10:09, Torbjörn SVENSSON wrote: > In GCC r17-2048-gcc195f7b11a406, support for resolving /etc/localtime symlinks > was added. As a consequense, when using semihosting for arm-none-eabi, the > resolution fails due to that semihosting unconditionally consider all paths as > character devices. This commit tries to work around that by inferring as much > stat details as possible from available APIs in the semihosting specification. > > With this change, I see that the failure is resolved, but there are a few > tests that have been marked as xfail that now passes. I will follow up > with a patch for GCC as soon as this change is merged. > > These are the changes when testing with GCC r17-2833-g32657f29f91871. > > Without patch: > PASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 (test for excess errors) > FAIL: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 execution test > PASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 execution test > PASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 (test for excess errors) > XFAIL: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 execution test > PASS: std/time/tzdb/1.cc -std=gnu++20 (test for excess errors) > FAIL: std/time/tzdb/1.cc -std=gnu++20 execution test > PASS: std/time/zoned_time/custom.cc -std=gnu++20 (test for excess errors) > FAIL: std/time/zoned_time/custom.cc -std=gnu++20 execution test > > With patch: > PASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/1-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/1-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/2-in.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_filebuf/sgetn/char/2-io.cc -std=gnu++20 execution test > PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 (test for excess errors) > PASS: 27_io/basic_filebuf/underflow/wchar_t/11603.cc -std=gnu++20 execution test > PASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_istream/readsome/char/6746-2.cc -std=gnu++20 execution test > PASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 (test for excess errors) > XPASS: 27_io/basic_istream/readsome/wchar_t/6746-2.cc -std=gnu++20 execution test > PASS: std/time/tzdb/1.cc -std=gnu++20 (test for excess errors) > PASS: std/time/tzdb/1.cc -std=gnu++20 execution test > PASS: std/time/zoned_time/custom.cc -std=gnu++20 (test for excess errors) > PASS: std/time/zoned_time/custom.cc -std=gnu++20 execution test > > > These is also an implementation choise to be made. > Would it be prefered to use stack (like I do in this patch) or would it be > better to allocated the temporary string in path_is_dir on the heap using > malloc? Regardless of solution, semihosting is expensive, so I don't think > it really matters what solution is used. I choose stack based for the first > version as it does not pull in any extra functions. > > Ok to push to master as-is or should I change to a heap based implementation? > > Kind regards, > Torbjörn > > -- > > Arm semihosting does not provide a real stat operation, so _stat has to > infer file information from the operations that are available. > > The previous code always treated an opened path as a regular file, and > _swistat then added S_IFCHR unconditionally. That meant directories were > not reported as directories, and the file type bits could end up > describing more than one kind of file at once. > > Use _isatty to identify character devices, use SYS_FLEN for regular > files, and probe "path/." so _stat can recognize directories. > > This gives callers such as std::filesystem::canonical enough information > to handle semihosted paths correctly. > > Discovered, and reported, in > https://gcc.gnu.org/pipermail/gcc-patches/2026-August/727185.html > > Signed-off-by: Torbjörn SVENSSON <torbjorn.svensson@foss.st.com> > --- > libgloss/arm/syscalls.c | 79 ++++++++++++++++++++++++++++++++++++++--- > 1 file changed, 74 insertions(+), 5 deletions(-) > > diff --git a/libgloss/arm/syscalls.c b/libgloss/arm/syscalls.c > index 710a741ee..162059fc0 100644 > --- a/libgloss/arm/syscalls.c > +++ b/libgloss/arm/syscalls.c > @@ -15,6 +15,7 @@ > #include <reent.h> > #include <unistd.h> > #include <sys/wait.h> > +#include <limits.h> > #include "swi.h" > > /* Forward prototypes. */ > @@ -46,6 +47,7 @@ void initialise_monitor_handles (void); > static int checkerror (int); > static int error (int); > static int get_errno (void); > +static int path_is_dir (const char *); > > /* Semihosting utilities. */ > static void initialise_semihosting_exts (void); > @@ -346,6 +348,46 @@ checkerror (int result) > return result; > } > > +/* Check if the given path is likely a directory. */ > +static int > +path_is_dir (const char *path) > +{ > + size_t path_len; > + size_t dir_len; > + char dir_path[PATH_MAX + 3]; > + int fd; > + > + path_len = strlen (path); > + if (path_len == 0) > + return 0; > + > + if (path_len + 3 > sizeof (dir_path)) > + { > + errno = ENAMETOOLONG; > + return -1; > + } > + > + /* Build a new string that ends with "/.". */ > + memcpy (dir_path, path, path_len); > + dir_len = path_len; > + if (dir_path[dir_len - 1] != '/') > + dir_path[dir_len++] = '/'; > + dir_path[dir_len++] = '.'; > + dir_path[dir_len] = '\0'; > + > + /* Try to open the directory. */ > + fd = _open (dir_path, O_RDONLY); > + > + /* Error means that path either does not exist > + * or is not a directory. */ > + if (fd == -1) > + return 0; > + > + /* Not interested in the error. */ > + _close (fd); > + return 1; > +} > + > /* fh, is a valid internal file handle. > ptr, is a null terminated string. > len, is the length in bytes to read. > @@ -738,10 +780,14 @@ _swistat (int fd, struct stat * st) > return -1; > } > > - /* Always assume a character device, > - with 1024 byte blocks. */ > - st->st_mode |= S_IFCHR; > st->st_blksize = 1024; > + if (_isatty (fd)) > + { > + /* Assume character device. */ > + st->st_mode |= S_IFCHR; > + return 0; > + } > + > #ifdef ARM_RDI_MONITOR > res = checkerror (do_AngelSWI (AngelSWI_Reason_FLen, &pfd->handle)); > #else > @@ -752,7 +798,19 @@ _swistat (int fd, struct stat * st) > checkerror (res); > #endif > if (res == -1) > - return -1; > + { > + if (errno == EISDIR) > + { > + /* Path is a directory. */ > + st->st_mode |= S_IFDIR; > + return 0; > + } > + return -1; > + } > + > + /* Assume regular file. */ > + st->st_mode |= S_IFREG; > + > /* Return the file size. */ > st->st_size = res; > return 0; > @@ -770,12 +828,23 @@ _stat (const char *fname, struct stat *st) > { > int fd, res; > memset (st, 0, sizeof (* st)); > + res = path_is_dir (fname); > + if (res == 1) > + { > + st->st_mode = S_IFDIR | S_IREAD | S_IEXEC; > + st->st_blksize = 1024; > + return 0; > + } > + else if (res == -1) > + return -1; > + > /* The best we can do is try to open the file readonly. If it exists, > then we can guess a few things about it. */ > if ((fd = _open (fname, O_RDONLY)) == -1) > return -1; > - st->st_mode |= S_IFREG | S_IREAD; > res = _swistat (fd, st); > + if (res == 0) > + st->st_mode |= S_IREAD; > /* Not interested in the error. */ > _close (fd); > return res;
diff --git a/libgloss/arm/syscalls.c b/libgloss/arm/syscalls.c index 710a741ee..162059fc0 100644 --- a/libgloss/arm/syscalls.c +++ b/libgloss/arm/syscalls.c @@ -15,6 +15,7 @@ #include <reent.h> #include <unistd.h> #include <sys/wait.h> +#include <limits.h> #include "swi.h" /* Forward prototypes. */ @@ -46,6 +47,7 @@ void initialise_monitor_handles (void); static int checkerror (int); static int error (int); static int get_errno (void); +static int path_is_dir (const char *); /* Semihosting utilities. */ static void initialise_semihosting_exts (void); @@ -346,6 +348,46 @@ checkerror (int result) return result; } +/* Check if the given path is likely a directory. */ +static int +path_is_dir (const char *path) +{ + size_t path_len; + size_t dir_len; + char dir_path[PATH_MAX + 3]; + int fd; + + path_len = strlen (path); + if (path_len == 0) + return 0; + + if (path_len + 3 > sizeof (dir_path)) + { + errno = ENAMETOOLONG; + return -1; + } + + /* Build a new string that ends with "/.". */ + memcpy (dir_path, path, path_len); + dir_len = path_len; + if (dir_path[dir_len - 1] != '/') + dir_path[dir_len++] = '/'; + dir_path[dir_len++] = '.'; + dir_path[dir_len] = '\0'; + + /* Try to open the directory. */ + fd = _open (dir_path, O_RDONLY); + + /* Error means that path either does not exist + * or is not a directory. */ + if (fd == -1) + return 0; + + /* Not interested in the error. */ + _close (fd); + return 1; +} + /* fh, is a valid internal file handle. ptr, is a null terminated string. len, is the length in bytes to read. @@ -738,10 +780,14 @@ _swistat (int fd, struct stat * st) return -1; } - /* Always assume a character device, - with 1024 byte blocks. */ - st->st_mode |= S_IFCHR; st->st_blksize = 1024; + if (_isatty (fd)) + { + /* Assume character device. */ + st->st_mode |= S_IFCHR; + return 0; + } + #ifdef ARM_RDI_MONITOR res = checkerror (do_AngelSWI (AngelSWI_Reason_FLen, &pfd->handle)); #else @@ -752,7 +798,19 @@ _swistat (int fd, struct stat * st) checkerror (res); #endif if (res == -1) - return -1; + { + if (errno == EISDIR) + { + /* Path is a directory. */ + st->st_mode |= S_IFDIR; + return 0; + } + return -1; + } + + /* Assume regular file. */ + st->st_mode |= S_IFREG; + /* Return the file size. */ st->st_size = res; return 0; @@ -770,12 +828,23 @@ _stat (const char *fname, struct stat *st) { int fd, res; memset (st, 0, sizeof (* st)); + res = path_is_dir (fname); + if (res == 1) + { + st->st_mode = S_IFDIR | S_IREAD | S_IEXEC; + st->st_blksize = 1024; + return 0; + } + else if (res == -1) + return -1; + /* The best we can do is try to open the file readonly. If it exists, then we can guess a few things about it. */ if ((fd = _open (fname, O_RDONLY)) == -1) return -1; - st->st_mode |= S_IFREG | S_IREAD; res = _swistat (fd, st); + if (res == 0) + st->st_mode |= S_IREAD; /* Not interested in the error. */ _close (fd); return res;