Erros comuns do compilador

Esta seção ilustra os erros típicos do compilador que ocorrem ao migrar uma base de código existente. Esses exemplos são do código HAL no nível do sistema, embora os conceitos sejam diretamente aplicáveis ao código no nível do usuário.

Exemplo 1 do aviso C4311

'type cast': truncamento de ponteiro de 'void *__ptr64' para 'unsigned long

Código

pPciAddr->u.AsULONG = (ULONG) CIA_PCI_CONFIG_BASE_QVA;

Descrição

PtrToUlong é uma função ou macro embutida, dependendo do seu uso. Ele trunca um ponteiro para um ULONG. Embora ponteiros de 32 bits não sejam afetados, a metade superior de um ponteiro de 64 bits é truncada.

CIA_PCI_CONFIG_BASE_QVA é declarado como um PVOID. A conversão ULONG funciona no mundo de 32 bits, mas resulta em um erro no mundo de 64 bits. A solução é obter um ponteiro de 64 bits para um ULONG, pois alterar a definição da união que pPciAddr-u.AsULONG> é definida em altera muito código.

O uso da macro PtrToUlong para converter o PVOID de 64 bits no ULONG necessário é permitido porque temos conhecimento sobre o valor específico de CIA_PCI_CONFIG_BASE_QVA. Nesse caso, esse ponteiro nunca terá dados nos 32 bits superiores.

Solução

pPciAddr->u.AsULONG = PtrToUlong(CIA_PCI_CONFIG_BASE_QVA);

Aviso C4311 Exemplo 2

'type cast': truncamento de ponteiro de 'struct _ERROR_FRAME *__ptr64 ' para 'unsigned long

Código

KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG)PUncorrectableError );

Descrição

O problema é que o último parâmetro para essa função é um ponteiro para uma estrutura de dados. Como PUncorrectableError é um ponteiro, ele altera o tamanho com o modelo de programação. O protótipo de KeBugCheckEx foi alterado para que o último parâmetro seja um ULONG_PTR. Como resultado, é necessário converter o ponteiro da função em um ULONG_PTR.

Você pode perguntar por que PVOID não foi usado como o último parâmetro. Dependendo do contexto da chamada, o último parâmetro pode ser algo diferente de um ponteiro ou talvez um código de erro.

Solução

KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG_PTR)PUncorrectableError );

Exemplo 1 do aviso C4244

'=' : conversão de 'struct _CONFIGURATION_COMPONENT *__ptr64 ' em 'struct _CONFIGURATION_COMPONENT *', possível perda de dados

Código

Component = &CurrentEntry->ComponentEntry;

Descrição

A função declara a variável Component como um PCONFIGURATION_COMPONENT. Posteriormente, a variável é usada na seguinte atribuição que parece correta:

Component = &CurrentEntry->ComponentEntry;

No entanto, o tipo PCONFIGURATION_COMPONENT é definido como:

typedef struct __CONFIGURATION_COMPONENT {
...
...
} CONFIGURATION_COMPONENT, * POINTER_32 PCONFIGURATION_COMPONENT;

A definição de tipo para PCONFIGURATION_COMPONENT fornece um ponteiro de 32 bits em modelos de 32 bits e 64 bits porque é declarado POINTER_32. O designer original dessa estrutura sabia que ela seria usada em um contexto de 32 bits no BIOS e definiu-a expressamente para esse uso. Esse código funciona bem no Windows de 32 bits porque os ponteiros são de 32 bits. No Windows de 64 bits, ele não funciona porque o código está no contexto de 64 bits.

Solução

Para contornar esse problema, use CONFIGURATION_COMPONENT * em vez do PCONFIGURATION_COMPONENT de 32 bits. É importante entender claramente a finalidade do código. Se esse código for destinado a tocar no BIOS de 32 bits ou no espaço do sistema, essa correção não funcionará.

POINTER_32 é definido em Ntdef.h e Winnt.h.

#ifdef (__AXP64__)
#define POINTER_32 _ptr32
#else
#define POINTER_32
#endif

Exemplo 2 do aviso C4242

'=' : conversão de '__int64' para 'unsigned long', possível perda de dados

Código

ARC_STATUS HalpCopyNVRamBuffer (
IN PCHAR NvDestPtr,
IN PCHAR NvSrcPtr,
IN ULONG Length )
{

ULONG PageSelect1, ByteSelect1;

ByteSelect1 = (NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;

ByteSelect1 = (NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;

Descrição

Esse aviso é gerado porque o cálculo está usando valores de 64 bits, nesse caso, ponteiros e colocando o resultado em um ULONG de 32 bits.

Solução

Digite converter o resultado do cálculo em um ULONG , conforme mostrado aqui:

ByteSelect1 = (ULONG)(NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;

O typecasting do resultado permite que o compilador saiba que você tem certeza sobre o resultado. Dito isso, certifique-se de que você entenda o cálculo e realmente tenha certeza de que ele se encaixará em um ULONG de 32 bits.

Se o resultado não se ajustar a um ULONG de 32 bits, altere o tipo base da variável que conterá o resultado.

Aviso C4311 – Exemplo 1

'type cast': truncamento de ponteiro de 'void *__ptr64 ' para 'unsigned long'

Código

ULONG HalpMapDebugPort(
IN ULONG ComPort,
OUT PULONG ReadQva,
OUT PULONG WriteQva)
{
ULONG ComPortAddress;

ULONG PortQva;

// Compute the port address, based on the desired com port.

switch( ComPort ){
case 1:
   ComPortAddress = COM1_ISA_PORT_ADDRESS;
   break;

case 2:
default:
   ComPortAddress = COM2_ISA_PORT_ADDRESS;
}
PortQva = (ULONG)HAL_MAKE_QVA(CIA_PCI_SPARSE_IO_PHYSICAL) + ComPortAddress;

// Return the QVAs for read and write access.

*ReadQva = PortQva;
*WriteQva = PortQva;

return ComPortAddress;
}

Descrição

Toda essa função lida com endereços como inteiros, exigindo a necessidade de digitar esses inteiros de maneira portátil. Todas as variáveis locais, valores intermediários em cálculos e valores retornados devem ser tipos portáteis.

Solução

ULONG_PTR HalpMapDebugPort(
IN ULONG ComPort,
OUT PULONG_PTR ReadQva,
OUT PULONG_PTR WriteQva)
{
ULONG_PTR ComPortAddress;

ULONG_PTR PortQva;

// Compute the port address, based on the desired com port.

switch( ComPort ){
case 1:
   ComPortAddress = COM1_ISA_PORT_ADDRESS;
   break;

case 2:
default:
   ComPortAddress = COM2_ISA_PORT_ADDRESS;
}

PortQva = (ULONG_PTR)HAL_MAKE_QVA(CIA_PCI_SPARSE_IO_PHYSICAL) + ComPortAddress;

// Return the QVAs for read and write access.

*ReadQva = PortQva;
*WriteQva = PortQva;

return ComPortAddress;
}

PULONG_PTR é um ponteiro de 32 bits para Windows de 32 bits e 64 bits para Windows de 64 bits. Ele aponta para um inteiro sem sinal, ULONG_PTR, que é de 32 bits para Windows de 32 bits e 64 bits para Windows de 64 bits.

Aviso C4311 – Exemplo 2

'type cast': truncamento de ponteiro de 'void *__ptr64 ' para 'unsigned long'

Código

BOOLEAN
HalpMapIoSpace (
VOID
)
{
PVOID PciIoSpaceBase;
PciIoSpaceBase = HAL_MAKE_QVA( CIA_PCI_SPARSE_IO_PHYSICAL );

//Map base addresses in QVA space.

HalpCMOSRamBase = (PVOID)((ULONG)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);

Descrição

Embora todos os valores de QVA (Endereço Virtual Quase) sejam realmente valores de 32 bits neste estágio e se ajustem a um ULONG, é mais consistente tratar todos os endereços como valores ULONG_PTR quando possível.

O ponteiro PciIoSpaceBase contém a QVA criada na macro HAL_MAKE_QVA. Essa macro retorna um valor de 64 bits com os 32 bits principais definidos como zero para que a matemática funcione. Poderíamos simplesmente deixar o código para truncar o ponteiro em um ULONG, mas essa prática não é recomendada para aprimorar a capacidade de manutenção e portabilidade do código. Por exemplo, o conteúdo de uma QVA pode mudar no futuro para usar alguns dos bits superiores nesse nível, quebrando o código.

Solução

Seja seguro e use ULONG_PTR para todos os cálculos de endereço e ponteiro.

HalpCMOSRamBase = (PVOID)((ULONG_PTR)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);

Aviso C4311 Exemplo 3

'type cast': truncamento de ponteiro de 'void *__ptr64 ' para 'unsigned long'

Código

PVOID
HalDereferenceQva(
PVOID Qva,
INTERFACE_TYPE InterfaceType,
ULONG BusNumber)

if ( ((ULONG) Qva & QVA_SELECTORS) == QVA_ENABLE ) {

return( (PVOID)( (ULONG)Qva << IO_BIT_SHIFT ) );
} 
else {
return (Qva);
}

Descrição

O compilador avisará sobre o endereço dos operadores (&) e shift esquerdo (<<) se eles forem aplicados a tipos de ponteiro. No código acima, Qva é um valor PVOID . Precisamos converter isso em um tipo inteiro para executar a matemática. Como o código deve ser portátil, use ULONG_PTR em vez de ULONG.

Solução

if ( ((ULONG_PTR) Qva & QVA_SELECTORS) == QVA_ENABLE ) {
  return( (PVOID)( (ULONG_PTR)Qva << IO_BIT_SHIFT ) );

Aviso C4311 Exemplo 4

'type cast': truncamento de ponteiro de 'void *__ptr64 ' para 'unsigned long'

Código

TranslatedAddress->LowPart = (ULONG)HalCreateQva(
*TranslatedAddress, va);

Descrição

TranslatedAddress é uma união semelhante à seguinte:

typedef union
   Struct {
      ULONG LowPart;
      LONG Highpart;
   }
   LONGLONG QuadPart;
}

Solução

Sabendo o que o restante do código pode colocar em Highpart, podemos selecionar qualquer uma das soluções mostradas aqui.

TranslatedAddress->LowPart = PtrToUlong(HalCreateQva(*TranslatedAddress,va) );

A macro PtrToUlong trunca o ponteiro retornado por HalCreateQva para 32 bits. Sabemos que a QVA retornada por HalCreateQva tem os 32 bits superiores definidos como zero e a próxima linha de códigos define TranslatedAddress-Highpart> como zero.

Com cuidado, podemos usar o seguinte:

TranslatedAddress->QuadPart = (LONGLONG)HalCreateQva(*TranslatedAddress,va);

Isso funciona neste exemplo: a macro HalCreateQva está retornando 64 bits, com os 32 bits superiores definidos como zero. Apenas tenha cuidado para não deixar os 32 bits superiores indefinidos em um ambiente de 32 bits, o que essa segunda solução pode realmente fazer.

Aviso C4311 Exemplo 5

'type cast': truncamento de ponteiro de 'void *__ptr64' para 'unsigned long'

Código

VOID
HalpCiaProgramDmaWindow(
PWINDOW_CONTROL_REGISTERS WindowRegisters,
PVOID MapRegisterBase
)
{
CIA_WBASE Wbase;

Wbase.all = 0;
Wbase.Wen = 1;
Wbase.SgEn = 1;
Wbase.Wbase = (ULONG)(WindowRegisters->WindowBase) >> 20;

Descrição

WindowRegisters-WindowBase> é um ponteiro e agora tem 64 bits. O código diz para deslocar à direita esse valor de 20 bits. O compilador não nos permitirá usar o operador de deslocamento à direita (>>) em um ponteiro; portanto, precisamos convertê-lo em algum tipo de inteiro.

Solução

Wbase.Wbase= PtrToUlong ( (PVOID) ((ULONG_PTR) (WindowRegisters->WindowBase) >> 20));

Converter em um ULONG_PTR é exatamente o que precisamos. O próximo problema é o Wbase. O Wbase é um ULONG e tem 32 bits. Nesse caso, sabemos que o ponteiro de 64 bits WindowRegisters-WindowBase> é válido nos 32 bits inferiores, mesmo depois de ser deslocado. Isso torna aceitável o uso da macro PtrToUlong , pois ela truncará o ponteiro de 64 bits em um ULONG de 32 bits. A conversão PVOID é necessária porque PtrToUlong espera um argumento de ponteiro. Quando você olha para o código assembler resultante, toda essa conversão de código C se torna apenas um quad de carga, desloca para a direita e armazena longa.