web-dev-qa-db-ja.com

gcc構造体内のメモリアライメント

アプリケーションをCのARMプラットフォームに移植しています。アプリケーションもx86プロセッサで実行され、下位互換性が必要です。

現在、変数の配置に問題があります。 __attribute__((aligned(4),packed))のgccマニュアルを読みました。構造体の開始が4バイトの境界に揃えられ、パックされたステートメントのために内部が変更されていないため、言われていることを解釈します。

もともと私はこれを持っていましたが、時々それは4バイトの境界と整列せずに配置されます。

typedef struct  
{  
 unsigned int code;  
 unsigned int length;  
 unsigned int seq;  
 unsigned int request;  
 unsigned char nonce[16];  
 unsigned short  crc;  
} __attribute__((packed)) CHALLENGE;

だからこれに変更します。

typedef struct  
{  
 unsigned int code;  
 unsigned int length;  
 unsigned int seq;  
 unsigned int request;  
 unsigned char nonce[16];  
 unsigned short  crc;  
} __attribute__((aligned(4),packed)) CHALLENGE;

構造体が4バイト境界に整列され、内部データが4バイト境界に整列されているため、前に述べた理解は正しくないようですが、エンディアンのために、構造体のサイズが大きくなっています。サイズは42〜44バイトです。構造体が42バイトであることに依存する他のアプリケーションがあるため、このサイズは重要です。

必要な操作を実行する方法を説明してもらえますか。どんな助けでも大歓迎です。

13
Mumbles

sizeof(yourstruct)が42バイトであることに依存している場合は、移植性のない仮定の世界に噛まれようとしています。これが何のためにあるのかについては述べていませんが、構造体の内容のエンディアンも重要である可能性が高いため、x86との不一致もある可能性があります。

この状況で対処する唯一の確実な方法は、重要な部分でunsigned char[42]を使用することだと思います。この42バイトのブロックのどこにどのフィールドがあり、どのエンディアンであるかを正確に指定することから始め、その定義を使用して、それと相互作用できる構造体との間で変換するコードを記述します。コードは、オールアットワンスシリアル化コード(別名マーシャリング)、または一連のゲッターとセッターのいずれかになる可能性があります。

12
crazyscot

これが、メンバーごとではなく構造体全体の読み取りが失敗する理由の1つであり、避ける必要があります。

この場合、パッキングと4での整列は、2バイトのパディングがあることを意味します。これは、すべてのアイテムが4に整列されたまま、配列に型を格納するためにサイズに互換性がなければならないために発生します。

私はあなたが次のようなものを持っていると想像します:

read(fd, &obj, sizeof obj)

異なるデータに属する2つのパディングバイトを読み取りたくないため、サイズを明示的に指定する必要があります。

read(fd, &obj, 42)

保守可能に保つことができるもの:

typedef struct {
  //...
  enum { read_size = 42 };
} __attribute__((aligned(4),packed)) CHALLENGE;

// ...

read(fd, &obj, obj.read_size)

または、CでC++の一部の機能を使用できない場合:

typedef struct {
  //...
} __attribute__((aligned(4),packed)) CHALLENGE;
enum { CHALLENGE_read_size = 42 };

// ...

read(fd, &obj, CHALLENGE_read_size)

次のリファクタリングの機会に、関数内に簡単にカプセル化できる各メンバーを個別に読み始めることを強くお勧めします。

5
Roger Pate

あなたの本当の目標は何ですか?

ファイル内または特定の形式でネットワーク上にあるデータを処理する場合は、内部のデータをどのように処理するかを表すコンパイラ構造体間でデータを移動するマーシャリング/シリアル化ルーチンを作成する必要があります。プログラムと、データがワイヤ/ファイル上でどのように見えるかを処理するchar配列。

次に、慎重に処理する必要があり、プラットフォーム固有のコードを使用する必要があるのは、マーシャリングルーチンだけです。また、現在および将来どのプラットフォームに移植する必要があるかに関係なく、マーシャリングされたデータが構造体との間で適切にやり取りされることを確認するために、Nice-n-nastyユニットテストを作成できます。

2
Michael Burr

Linux、Windows、Mac、C、Swift、Assemblyなどから構造を前後に移動してきました。

問題はそれができないということではありません、問題はあなたが怠惰になることができず、あなたのツールを理解しなければならないということです。

なぜ使用できないのかわかりません:

_typedef struct  
{  
 unsigned int code;  
 unsigned int length;  
 unsigned int seq;  
 unsigned int request;  
 unsigned char nonce[16];  
 unsigned short  crc;  
} __attribute__((packed)) CHALLENGE;
_

あなたcanそれを使用し、特別なコードや巧妙なコードは必要ありません。 ARMと通信するコードをたくさん書いています。構造は物事を機能させるものです。 __attribute__ ((packed))は私の友達です。

両方で何が起こっているのかを理解していれば、「傷ついた世界」にいる可能性はゼロです。

最後に、42または44を取得する方法を一生理解することはできません。Intは4バイトまたは8バイトのいずれかです(コンパイラによって異なります)。これにより、数値は16 + 16 + 2 = 34または32+ 16 + 2 = 50のいずれかになります。

私が言うように、あなたのツールを知ることはあなたの問題の一部です。

1
Cat Sargent

問題は、42が4で割り切れないことだと思います。したがって、これらの構造体のいくつかを背中合わせに配置すると、それらは整列しなくなります(たとえば、それらのいくつかにメモリを割り当て、sizeof)。サイズを44にすると、これらの場合、要求どおりに位置合わせが強制されます。ただし、各構造体メンバーの内部オフセットが同じままである場合は、44バイトの構造体を42バイトであるかのように扱うことができます(後続のデータを正しい境界に揃えるのに注意する限り)。

試してみるトリックの1つは、これらの構造体のbothを単一の共用体型内に配置し、そのような各共用体内からのみ42バイトバージョンを使用することです。

0
Arkku