So platzieren Sie eine Variable an einer bestimmten absoluten Adresse im Speicher (mit GCC)

Lesezeit: 7 Minuten

Benutzer-Avatar
Bas van Dijk

Der RealView ARM C-Compiler unterstützt Platzieren einer Variablen an einer gegebenen Speicheradresse unter Verwendung des Variablenattributs at(address):

int var __attribute__((at(0x40001000)));
var = 4;   // changes the memory located at 0x40001000

Hat GCC ein ähnliches Variablenattribut?

  • Ich schrieb ein Artikel, wo ich die Mittel dazu aufzähle. Könnte für manche nützlich sein.

    – a3f

    8. Mai 2016 um 21:13 Uhr

Benutzer-Avatar
Prof. Falken

Ich weiß es nicht, aber Sie können leicht eine Problemumgehung wie folgt erstellen:

int *var = (int*)0x40001000;
*var = 4;

Es ist nicht exakt das gleiche, aber in den meisten Situationen ein perfekter Ersatz. Es funktioniert mit jedem Compiler, nicht nur mit GCC.

Wenn Sie GCC verwenden, gehe ich davon aus, dass Sie es auch verwenden GNU-ld (obwohl es natürlich keine Gewissheit ist) und ld unterstützt das Platzieren von Variablen wo immer du sie willst.

Ich stelle mir vor, dass es ziemlich üblich ist, den Linker diese Arbeit erledigen zu lassen.

Inspiriert von der Antwort von @rib füge ich hinzu, dass ich hinzufügen würde, wenn die absolute Adresse für ein Steuerregister bestimmt ist volatile zur Zeigerdefinition. Wenn es nur RAM ist, spielt es keine Rolle.

  • @roe: Es ist ein ziemlich normaler Trick, der in Gerätetreibern für Hardware mit festen speicherabgebildeten Steuerregistern verwendet werden kann. In einer Standardbenutzeranwendung hat es keinerlei Nutzen, den ich mir vorstellen kann.

    – JeremyP

    1. November 2010 um 9:46 Uhr

  • @JeremyP, bei eingebetteten Geräten, insbesondere solchen ohne MMU, ist es allzu üblich, die “Benutzeranwendungen” auf die Hardware treffen zu lassen.

    – Prof. Falken

    1. November 2010 um 9:47 Uhr

  • @ JeremyP; Das war mehr oder weniger mein Punkt, die Frage gibt nicht an, ob auf den Speicher auf diese Weise zugegriffen werden kann oder ob der Compiler bestimmte Aktionen ausführen muss, um dies zu erreichen.

    – Falstro

    1. November 2010 um 9:59 Uhr

  • @Prof.Falken: Ich finde gewisse Dinge unglücklich; Früher legten Compiler das zugrunde liegende Hardwareverhalten für die meisten Formen von UB offen, während heute eher eine Tendenz zu UB-basierten “Optimierungen” besteht. Früher war das so (x<<n) | (x>>(32-n)) war die kanonische Art, eine “Linksdrehung” durchzuführen, und es funktionierte früher auf 99,999% der Plattformen, auf denen x ein unsignierter 32-Bit-Typ sein konnte (ich kenne keine Ausnahmen außer denen, die für den außergewöhnlich pedantischen Modus konfiguriert sind), aber einige der heutigen Compiler werden diesen Ausdruck untersuchen und daraus schließen, dass x nicht Null sein kann. Historisch gesehen gab es nur…

    – Superkatze

    27. Oktober 2015 um 14:31 Uhr


  • Ich würde den Typ des Zeigers auf ändern int *constsodass der Compiler die Dereferenzierung wegoptimieren kann.

    – a3f

    8. Mai 2016 um 21:17 Uhr

Du könntest die verwenden Abschnittsattribute und ein ld Linker-Skript um die gewünschte Adresse für diesen Abschnitt zu definieren. Dies ist wahrscheinlich chaotischer als Ihre Alternativen, aber es ist eine Option.

  • Beachten Sie, dass dieser Ansatz eigentlich reservieren Platz für die Variable, anstatt einfach anzunehmen, dass sie an der angegebenen Adresse existiert. In vielen Fällen ist dies gewünscht.

    Benutzer1896626

    29. Januar 2015 um 6:03 Uhr


  • Ein direkterer Link für die Abschnittsattribute: gcc.gnu.org/onlinedocs/gcc/… und Strg+F für “Abschnitt”

    – Ponkadoodle

    31. Juli 2015 um 20:56 Uhr

  • Leider versuchen Tools manchmal, die Dinge zu vereinfachen, indem sie das Linker-Skript ausblenden/automatisch generieren, sodass es schwierig ist, es zu ändern (ich schaue auf Sie Arduino und ATMEL Studio). Ich würde gerne einen Weg finden, eine Variable an einer festen Adresse zu verankern und den Platz in einem vorhandenen Segment nur mit Code zuweisen zu lassen. :/

    – Großer Josch

    11. November 2018 um 18:18 Uhr

  • Ich habe ein minimales Beispiel für ein lauffähiges Linker-Skript bereitgestellt unter: stackoverflow.com/questions/4067811/…

    – Ciro Santilli OurBigBook.com

    10. Januar 2019 um 12:21 Uhr

Benutzer-Avatar
Ciro Santilli OurBigBook.com

Beispiel für ein minimales lauffähiges Linker-Skript

Die Technik wurde erwähnt unter: https://stackoverflow.com/a/4081574/895245 aber jetzt werde ich jetzt ein konkretes Beispiel liefern.

Haupt c

#include <stdio.h>

int myvar __attribute__((section(".mySection"))) = 0x9ABCDEF0;

int main(void) {
    printf("adr %p\n", (void*)&myvar);
    printf("val 0x%x\n", myvar);
    myvar = 0;
    printf("val 0x%x\n", myvar);
    return 0;
}

link.ld

SECTIONS
{
  .mySegment 0x12345678 : {KEEP(*(.mySection))}
}

GitHub-Upstream.

Kompilieren und ausführen:

gcc -fno-pie -no-pie -o main.out -std=c99 -Wall -Wextra -pedantic link.ld main.c
./main.out

Ausgabe:

adr 0x12345678
val 0x9abcdef0
val 0x0

Wir sehen also, dass es an der gewünschten Adresse abgelegt wurde.

Ich kann nicht finden, wo dies im GCC-Handbuch dokumentiert ist, aber die folgende Syntax:

gcc link.ld main.c

scheint das angegebene Linker-Skript an das Standardskript anzuhängen, das verwendet werden würde.

-fno-pie -no-pie ist erforderlich, da die Ubuntu-Toolchain jetzt standardmäßig so konfiguriert ist, dass sie ausführbare PIE-Dateien generiert, was dazu führt, dass der Linux-Kernel die ausführbare Datei jedes Mal an einer anderen Adresse ablegt, was unser Experiment durcheinander bringt. Siehe auch: Was ist die Option -fPIE für positionsunabhängige ausführbare Dateien in gcc und ld?

TODO: Kompilierung erzeugt eine Warnung:

/usr/bin/x86_64-linux-gnu-ld: warning: link.ld contains output sections; did you forget -T?

Mache ich etwas falsch? Wie kann man es loswerden? Siehe auch: So entfernen Sie die Warnung: link.res enthält Ausgabeabschnitte; hast du vergessen -T?

Getestet auf Ubuntu 18.10, GCC 8.2.0.

  • Santilli: Gerade jetzt kommt man um die Linker-Warnung nicht herum. Siehe meine Antwort auf stackoverflow.com/questions/20185268/…

    – Hermannk

    15. Januar 2019 um 9:46 Uhr

Benutzer-Avatar
Rippe

Sie haben Ihre Frage beantwortet. In Ihrem obigen Link heißt es:

Mit dem GNU GCC Compiler können Sie nur Zeigerdefinitionen verwenden, um auf absolute Speicherstellen zuzugreifen. Zum Beispiel:

#define IOPIN0         (*((volatile unsigned long *) 0xE0028000))
IOPIN0 = 0x4;

Übrigens http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Variable-Attributes.html#Variable%20Attributes

    extern const uint8_t dev_serial[12];
    asm(".equ dev_serial, 0x1FFFF7E8");
/* or    asm("dev_serial = 0x1FFFF7E8"); */
    ...

    for (i = 0 ; i < sizeof(dev_serial); i++)
        printf((char *)"%02x ", dev_serial[i]);

  • Was fügt dies zu den vorhandenen Antworten hinzu?

    – Orangenhund

    1. Juni 2016 um 12:17 Uhr

  • Dieses Code-Snippet kann zwar die Frage lösen, inklusive Erklärung hilft wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für zukünftige Leser beantworten und diese Personen die Gründe für Ihren Codevorschlag möglicherweise nicht kennen. Bitte versuchen Sie auch, Ihren Code nicht mit erklärenden Kommentaren zu überladen, dies verringert die Lesbarkeit sowohl des Codes als auch der Erklärungen!

    – Kiste Kiste Kiste Kiste

    1. Juni 2016 um 13:06 Uhr

  • Dies fügt tatsächlich etwas hinzu, was die anderen Antworten nicht tun. Durch die Verwendung dieses Ansatzes können Sie ein Array mit fester Länge positionieren, während Sie es noch zulassen sizeof() um die Array-Größe zu erhalten.

    – Tom Tischler

    22. März 2018 um 8:44 Uhr

In GCC können Sie Variablen in bestimmten Abschnitten platzieren:

__attribute__((section (".foo"))) static uint8_t * _rxBuffer;

oder

static uint8_t * _rxBuffer __attribute__((section (".foo")));

und geben Sie dann die Adresse des Abschnitts in den GNU Linker Memory Settings an:

.foo=0x800000 

  • Was fügt dies zu den vorhandenen Antworten hinzu?

    – Orangenhund

    1. Juni 2016 um 12:17 Uhr

  • Dieses Code-Snippet kann zwar die Frage lösen, inklusive Erklärung hilft wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für zukünftige Leser beantworten und diese Personen die Gründe für Ihren Codevorschlag möglicherweise nicht kennen. Bitte versuchen Sie auch, Ihren Code nicht mit erklärenden Kommentaren zu überladen, dies verringert die Lesbarkeit sowohl des Codes als auch der Erklärungen!

    – Kiste Kiste Kiste Kiste

    1. Juni 2016 um 13:06 Uhr

  • Dies fügt tatsächlich etwas hinzu, was die anderen Antworten nicht tun. Durch die Verwendung dieses Ansatzes können Sie ein Array mit fester Länge positionieren, während Sie es noch zulassen sizeof() um die Array-Größe zu erhalten.

    – Tom Tischler

    22. März 2018 um 8:44 Uhr

Benutzer-Avatar
Yahya Tawil

Ich hatte ein ähnliches Problem. Ich wollte eine Variable in meinem definierten Abschnitt an einem speziellen Offset zuweisen. Gleichzeitig wollte ich, dass der Code portabel ist (keine explizite Speicheradresse in meinem C-Code). Also habe ich den RAM-Abschnitt im Linker-Skript definiert und ein Array mit der gleichen Länge meines Abschnitts definiert (.noinit Abschnitt hat eine Länge von 0x0F).

uint8_t no_init_sec[0x0f] __attribute__ ((section (".noinit")));

Dieses Array bildet alle Orte dieses Abschnitts ab. Diese Lösung ist nicht geeignet, wenn der Abschnitt groß ist, da die ungenutzten Stellen in dem zugewiesenen Array ein verschwendeter Platz im Datenspeicher sind.

1386090cookie-checkSo platzieren Sie eine Variable an einer bestimmten absoluten Adresse im Speicher (mit GCC)

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy