簡體   English   中英

為什么這兩個應該相同的指針指向不同的數據?

[英]Why do these two pointers that should be the same point to different data?

我在 GNU C 中為一個愛好操作系統編寫了一個 FAT16 驅動程序,我有一個這樣定義的結構:

struct directory_entry {
  uint8_t name[11];
  uint8_t attrib;
  uint8_t name_case;
  uint8_t created_decimal;
  uint16_t created_time;
  uint16_t created_date;
  uint16_t accessed_date;
  uint16_t ignore;
  uint16_t modified_time;
  uint16_t modified_date;
  uint16_t first_cluster;
  uint32_t length;
} __attribute__ ((packed));

我的印象是name將與整個結構位於同一地址,並且在那之后該attrib將是 11 個字節。 實際上, (void *)e.name - (void *)&e是 0, (void *)&e.attrib - (void *)&e是 11,其中estruct directory_entry類型。

在我的 kernel 中,指向e的 void 指針被傳遞給從磁盤讀取其內容的 function。 在此 function 之后, *(uint8_t *)&e為 80, *((uint8_t *)&e + 11為 8,正如磁盤上所預期的那樣。但是, e.name[0]e.attrib均為 0。

這里給出了什么? 我是否誤解了__attribute__ ((packed))的工作原理? 具有相同屬性的其他結構按我對 kernel 其他部分的預期工作。 如果需要,我可以發布完整來源的鏈接。

編輯:完整的源代碼在這個 gitlab 存儲庫中,在stack-overflow分支上。 相關部分是 src/kernel/main.c 的第 34 到 52 行。 我確定數據填充正確,因為我檢查*(uint8_t *)&e*((uint8_t *)&e + 11) 當我運行它時,該部分是 output :

(void *)e.name - *(void *)&e
  => 0
*(uint8_t *)&e
  => 80
e.name[0]
  => 0
(void *)&e.attrib - (void *)&e
  => 11
*((uint8_t *)&e + 11)
  => 8
e.attrib
  => 0

我對為什么e.name[0]*(uint8_t *)&e有任何不同感到非常困惑。

編輯 2:我使用 objdump 反匯編了這部分,看看編譯后的代碼有什么不同,但現在我更加困惑了。 u8_dec(*(uint8_t *)&e, nbuf); u8_dec(e.name[0], nbuf); 都編譯為:(評論我的)

lea   eax, [ebp - 0x30] ;loads address of e from stack into eax
movzx eax, byte [eax]   ;loads byte pointed to by eax into eax, zero-extending
movzx eax, al           ;not sure why this is here, as it's already zero-extended
sub esp, 0x8
push  0x31ce0 ;nbuf
push  eax     ;the byte we loaded
call  0x3162f ;u8_dec
add esp, 0x10

正如預期的那樣,這會傳入結構的第一個字節。 我確信u8_dec不會修改 e,因為它的第一個參數是按值傳遞的,而不是按引用傳遞的。 nbuf是在文件范圍內聲明的數組,而e在 function scope 中聲明,因此它們不是重疊或任何東西。 也許u8_dec沒有做好它的工作? 這是它的來源:

void u8_dec(uint8_t n, uint8_t *b) {
  if (!n) {
    *(uint16_t *)b = '0';
    return;
  }
  bool zero = false;
  for (uint32_t m = 100; m; m /= 10) {
    uint8_t d = (n / m) % 10;
    if (zero)
      *(b++) = d + '0';
    else if (d) {
      zero = true;
      *(b++) = d + '0';
    }
  }
  *b = 0;
}

現在很清楚,打包結構確實按照我的想法工作,但我仍然不確定是什么導致了問題。 我將相同的值傳遞給應該是確定性的 function,但是在不同的調用中我得到不同的結果。

我的 kernel 使用 32 位保護模式分段。 我的數據段為 0x0000.0000 - 0x000f.ffff,堆棧段為 0x0003.8000 - 0x0003.ffff,以在堆棧溢出時觸發一般保護錯誤,而不是讓它溢出到其他 kernel 數據和代碼中.

但是,當 GCC 編譯 C 代碼時,它假定堆棧和數據段具有相同的基數,因為這是最常見的情況。 這導致了一個問題,因為當我獲取局部變量的地址時,它是相對於堆棧段的(因為局部變量在堆棧上),但是當我取消引用被調用的 function 中的指針時,它是相對於數據段。

我已經更改了我的分段 model 以便堆棧位於數據段而不是它自己的段中,這已經解決了問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM