Wednesday, December 30, 2015

Chinese Text in XBMC Cannot Display on Mac OS

English is the default language in XBMC.


After you chose Language as Chinese (Traditional), the skin displays strangely. 


Please change Language back to English.
Go to Appearance - Settings, and change Fonts from Skin default to Arial based.















Then the Chinese skin displays.














Then you can install the Add-On, AtMovies Movie Scraper.















-Count

Tuesday, December 29, 2015

Use Java Security to read a certificate generated by OpenSSL

The following example uses Java Security to read a certificate that is generated by OpenSSL command and to verify the certificate with the public key that is also generated by OpenSSL command.

Generate a RSA-2048 private kay.
>openssl genrsa -out prv.pem 2048
>openssl rsa -in prv.pem -pubout > pub.pem

Convert public key portino in DER format (so Java can read it)
>openssl rsa -in prv.pem -pubout -outform DER -out pub.der

Convert private key to PKCS#8 format (so Java can read it).
>openssl pkcs8 -topk8 -inform PEM -outform DER -in prv.pem -out prv.der -nocrypt

Generate a CSR signed by prv.pem
>openssl req -new -key prv.pem -out test.csr
password 1234

Generate a certificate, signed by prv.pem, for the CSR.
>openssl x509 -req -days 365 -in test.csr -signkey prv.pem -sha1 -out test.cert
password 1234

Generate another RSA-2048 private kay.
>openssl genrsa -out prv2.pem 2048

Convert public key portion in DER format (so Java can read it)
>openssl rsa -in prv2.pem -pubout -outform DER -out pub2.der

Run the java program to read certificate and to verify it.
D:\Cyber Space\Examples\JavaSecurity
>javac ReadCert.java

>java ReadCert

The portion Java program, ReadCert.java, is as below.

//
// Read an X.509 certificate from "test.cert".
//

FileInputStream fis = new FileInputStream ("test.cert");
BufferedInputStream bis = new BufferedInputStream (fis);
CertificateFactory cf = CertificateFactory.getInstance ("X.509");
if (bis.available () == 0) {
    System.exit (0);
}

//
// Dump the certificate.
//

java.security.cert.Certificate cert = cf.generateCertificate (bis);
System.out.println (cert.toString());

//
// Get public key of the certificate.
//

PublicKey pub = cert.getPublicKey ();
System.out.println ("Get the public key of the certificate with " + pub.getEncoded().length + " bytes.");

//
// Verify the cert with public key (pub).
//

System.out.println ("Verify the certificate with the public key.");
try {
    cert.verify (pub);
catch (Exception e) {
    System.out.println ("Exception.");
}

//
// Read public key (pub2) from the file (pub.der).
//

File f = new File ("pub.der");
fis = new FileInputStream (f);
DataInputStream dis = new DataInputStream (fis);
byte [] pubBlob = new byte [(int) f.length()];
System.out.println ("pubBlob.length = " + pubBlob.length);
dis.readFully (pubBlob);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pub2 = keyFactory.generatePublic(new X509EncodedKeySpec (pubBlob));
System.out.println ("Get the public key, pub2, from the file with " + pub2.getEncoded().length + " bytes.");

//
// Check if pub and pub2 are same.
// They should be same.
//

if (pub.equals (pub2)) {
    System.out.println ("pub and pub2 are same.");
} else {
    System.out.println ("pub and pub2 are different.");        
}

//
// Read another public key (pub3) from the file (pub2.der).
//

f = new File ("pub2.der");
fis = new FileInputStream (f);
dis = new DataInputStream (fis);
byte [] pub2Blob = new byte [(int) f.length()];
dis.readFully (pub2Blob);
keyFactory = KeyFactory.getInstance("RSA");
PublicKey pub3 = keyFactory.generatePublic(new X509EncodedKeySpec (pub2Blob));
System.out.println ("Get the public key, pub3, from the file with " + pub3.getEncoded().length + " bytes.");

//
// Verify the certificate with the public key (pub3).
// The verification should be failed.
//

System.out.println ("Verify the certificate with the public key, pub3.");
try {
    cert.verify (pub3);
catch (Exception e) {
    System.out.println ("Error. An exception occurs. The result is expected.");
}

-Count





Use Java Security to export/import key pair into/from memory blob

I provide the example program of Java Security to explain how to

  1. export RSA key pair into memory blob.
  2. import RSA key pair from memory blob.
This example helps us to handle certificate or public key, that are generated by OpenSSL tool, in Java environment.

// generate an RSA-2048 key
System.out.println( "\nGenerate RSA-2048 key pair." );
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair key = keyGen.generateKeyPair();

// Export key pair into memory blob.
System.out.println( "Export key pair into memory blob." );
PublicKey pub = key.getPublic ();
PrivateKey prv = key.getPrivate ();
byte[] pubBlob = pub.getEncoded();
byte[] prvBlob = prv.getEncoded();
System.out.println ("Public key with " + pubBlob.length + " bytes: " + pubBlob);
System.out.println ("Private key with " + prvBlob.length + " bytes: " + prvBlob);

// Import key pair from memory blob.
System.out.println( "Import key pair from memory blob." );
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pub2 = keyFactory.generatePublic (
                    new X509EncodedKeySpec(pubBlob));
PrivateKey prv2 = keyFactory.generatePrivate(
                    new PKCS8EncodedKeySpec(prvBlob));
KeyPair key2 = new KeyPair (pub2, prv2);

// Check if the two key pairs are same.
if (pub.equals (pub2) && prv.equals (prv2)) {
    System.out.println ("Both key pairs are same");
} else {
    System.out.println ("Both key pairs are different");
}

-Count    

Monday, December 28, 2015

Use Python to enable Bluetooth PAN in Windows

We cannot enable Windows Bluetooth PAN by a program because Windows doesn't public the Bluetooth PAN API that is implemented in bthpanapi.dll. I find a way to enable it by a script method, Python. The idea comes from UI automatic test.

Download and install Python 3.5.1

Install Pillow and PyAutoGUI in Python.
>pip install Pillow
>pip install PyAutoGUI

The Python program to enable Windows Bluetooth PAN is as below.


import os                                            
import pyautogui                                     
                                                     
os.system ("control printers")                       
                                                     
while (True):                                        
    Location = pyautogui.locateOnScreen('VIBEZ.png') 
    if (Location != None):                           
        break                                        
pyautogui.rightClick (Location [0], Location [1])    
                                                     
x = Location [0] + 10                                
y = Location [1] + 83                                
pyautogui.click (x, y)                               
                                                     
x += 230                                             
pyautogui.click (x, y)                               

The code opens "Devices and Printers" of Windows Control Panel.

os.system ("control printers")



The code moves the mouse cursor to the icon of "VIBE Z" and click the right button of the mouse to display menu items.

while (True):                                       
    Location = pyautogui.locateOnScreen('VIBEZ.png')
    if (Location != None):                          
        break                                       
pyautogui.rightClick (Location [0], Location [1])   



The code moves the mouse cursor to the 4th menu item.

x = Location [0] + 10  
y = Location [1] + 83  
pyautogui.click (x, y) 






The code moves the mouse cursor to the right menu item to enable Bluetooth PAN.

x += 230               
pyautogui.click (x, y) 




-Count



Java Security doesn't support AES-256 in default

The below code in AesExample.java generates an AES-256 key,

KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
Key key = keyGen.generateKey();

but the exception happens when running java AesExample.

The reason is the import regulation in some countries. AES is limited to 128 bits in default security policy.

AlgorithmMaximum Keysize
DES64
DESede*
RC2128
RC4128
RC5128
RSA*
all others128

We can change the security policy by downloading the zip from the site.

Please select "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files for JDK/JRE 8" to download the zip file, jce_policy-8.zip, if we use JDK 8.

Decompress the ZIP file to get the both jar files.
local_policy.jar
US_export_policy.jar

Copy them to the directory to replace old files.
JAVA_HOME\jre\lib\security

Now we can use AES-256 in Java environment.

>javac AesExample.java
>java AesExample

-Count

Tuesday, December 22, 2015

Why Windows CNG doesn't support AES in key storage functions?

The CNG key storage function, NCryptExportKey (), doesn't support BCRYPT_AES_WRAP_KEY_BLOB but the CNG cryptographic primitive function, BCryptExportKey(), supports it. Why Windows CNG doesn't support AES in key storage functions?

I guess that AES is symmetric encryption algorithm. The AES key is shared between two devices. For asymmetric encryption algorithm (e.g., RSA), only public key is shared and private key is kept and hidden in the owned device. The private key must be protected in a secure storage. Therefore Windows CNG provides key storage functions to store private keys of RSA. For AES key, it is not necessary to protect it. Therefore there is not AES in CNG key storage functions.

-Count

Sunday, December 20, 2015

Symmetric/Asymmetric Encryption and Hashing

I use the following simple formula to distinguish among symmetric/asymmetric encryption and hashing.

C = Enc (P, K1)
P = Dec (C, K2)

Where
C is cypher text,
P is plain text,
Enc is an encryption algorithm,
Dec is an decryption algorithm,
K1 is a key for encryption.
K2 is a key for decryption.

If K1 == K2, then Enc and Dec are symmetric encryption (e.g., AES).

If K1 != K2, then Enc and Dec are asymmetric encryption (e.g., RSA).

If Enc is an one-way function (there is not a Dec to recover C to P), we can call it hashing (e.g., SHA).

Use MacBook to Install Ubuntu in Gen8 via iLO

I don't have a monitor, a keyboard, and an ODD attached to my Gen8. I only have a SDD. How do I install Ubuntu in it? Fortunately I have MacBook to help it.

1. Connect network cables in Gen8.

Connect a network cable to the iLO connector and another one to the NIC 2 connector in the Gen8. (It is possible to use only one network cable to connect NIC 1 that is shared with iLO.)



2. Put SDD in the Gen8.

I use SDD to be a bootable disk. Because the hard drive tray is not suitable for my 2.5" SDD, I pull out the tray and connect SDD to the SATA port in the room. (The alternative way is connect SDD to the external USB port.)




3. Turn on the Gen8.

4. Check the IP address of the iLO in Gen8.

I open my router's web page to list all IP addresses of connected devices. I find one device is connected by a wire. I suppose it is for iLO.

5. Use Mozilla browser to connect the IP address and download Java plug-in.

Because Safari and Chrome browsers cannot verify the certificate of the iLO, some features are not workable. Therefore I download Mozilla browser to avoid the problem. The Remote Console of iLO must be run by Java in Mac OS. There is not Java in Mozilla in default. I need to download Java from Oracle web site.

6. Get a free license of iLO.

Because HP was separated into two companies, HP and HPE. It is difficult for me to find the free license of iLO. I need it to use Remote Console of iLO.

7. Activate Intelligent Provisioning.

Run Remote Console to display the Java applet.


It displays that the OOD doesn't exist.


Select "Activate" in the step.






8. Create a virtual ODD with a Ubuntu's installation image.

Run iLO Remote Console and click Virtual Drivers to create a virtual ODD from a image of Ubuntu. My image file name is ubuntu-14.04.3-desktop-amd64.iso, that is Ubuntu desktop for 64-bit. I select desktop rather than server because it has GUI in default.


9. Click Power Switch to restart Gen8 to install Ubuntu.


10. Run Ubuntu in iLO Remote Console.









Saturday, November 28, 2015

What happens when changing iPhone's passcode?

The iOS's passcode is one of the factors to encrypt files. We expected that it spends much time when we change our passcode because files need to be decrypted by old passcode and encrypted a new one. However changing passcode is prompt. Why?

The below picture comes from the document, iOS Security which I refer to answer the question even though the document doesn't have a direct answer.

The iOS uses the hierarchy keys to encrypt files. Class Key, that is used to encrypt and decrypt File Metadata, is encrypted by Hardware Key and Passcode Key. Hardware Key and File System Key are unique and constant. File Contents is encrypted and decrypted by a unique File Key that is in File Metadata that is encrypted and decrypted by File System Key and Class Key. Only Passcode Key can be changed by a user via the below steps.



  1. iOS uses Hardware Key and Passcode Key to decrypt the encrypted Class Key.
  2. The Passcode is update to new one.
  3. iOS uses Hardware Key and new one to encrypt the Class Key.

Because Class Key and File System Key are constant, changing of Passcode doesn't impact the encrypted file contents. This is the answer.

-Count 

Cannot Boot Ubuntu

The error, system is running in low-graphics mode, happens when booting Ubuntu. One reason for the error is that disk space is exhausted. We can boot Ubuntu in recovery mode and use the df command to check it.

df -h

if the disk is full, please use the command to find the largest directory.

du -sh -BM * | sort -g

Please use the command to remove the directory. For example,

rm -rf YOUR-DIR

Sometimes we cannot remove the directory because the file system is read-only. Please remount it by the command. For example,

sudo mount -o remount,rw /dev/YOUR-DISK

-Count

Wednesday, November 11, 2015

Why iPhone's passcode cannot be replaced with Touch ID

We are requested for inputing passcode rather than Touch ID when we restarting iPhone. Why? After I read iOS Security, I think I found one of the reasons.

The passcode in system memory (why?), which is disappear after we turn off iPhone, is charge of encrypting/decrypting files. However Touch ID cannot do the same thing. Why?

I explain the reason as follows.

  1. For security concern, the plaintext passcode cannot be stored in flash storage because it is easily leaked.
  2. The plaintext passcode in system memory disappears after turning off iPhone. Therefore there is no risk of leaking. Apps cannot read it at run-time because the memory's are is not readable and is encrypted by hardware (secure element.)
  3. A ciphertext passcode in flash is encrypted by a one-way algorithm (eg., SHA-1) from the plaintext one.
  4.  The iOS encrypts and decrypts files by the plaintext passcode.
  5. Why cannot we use Touch ID to encrypt files? Because the biometric data of our pressed fingers are different each time.


Monday, September 14, 2015

Evernote Synchronization is Failed

If you cannot solve the failure of Evernote synchronization in Windows environment after you try many ways, you can try to reset the IE.

Please refer it.
How to reset Internet Explorer settings.

-Count

Tuesday, September 1, 2015

Simply Introduce SSL

We can easily learn SSL from this page because it reduces some detail process of SSL. We can only focus on the kernel ideas. Therefore the below picture is very simple.





















Client want to find a efficient way to encrypt the request to Server. RSA is secure but no efficient. AES is efficient but no secure. The idea is to combine the strength of RSA with security and AES with efficient. The following steps simplify the SSL process.


  1. Client randomly generates an AES key, K.
  2. Client want to send K in a secure way. It uses RSA to encrypt it with the public key, PubS, of Server.
  3. Server uses RSA to decrypt the cipher with the private key, PrvS, of Server to get the key, K. We can make sure that the transformation of K is secure because it is encrypted.
  4. Now Client and Server have the same K. They can use K to encrypt request, M1 and response, M2 for communication in a secure way.
-Count  

How to Identify a User

This page describes the scenario of user identification. For example, how does a web site verify the user in web environment?

I separate the concept into 3 topics. First, we use the simplest but no efficient way to solve it. Second, we apply the digital signature way. Finally, we introduce secure boot that has the same concept.


Let's consider the use case. A client sends a request M1 to a server, and the server want to verify the M1 is sent by the the client. We can use asymmetric encryption/decryption to build a secure communication.
  1. The client generates key pairs, public key (PubC) and private key (PrvC).
  2. The client keeps PrvC and sends PubC to the server.
  3. Before the client sends the request M1, it encrypt M1 with PrvC to get Cipher.
  4. The client sends M1+Cipher to the server.
  5. The server decrypt Cipher with PubC to get M.
  6. The server compare M and M1. If both are same, the server make sure that M1 is owned by Client.
There is a problem that the RSA algorithm has bad performance for large data. We can apply digital signature to solve the performance.



We use Digest instead of Cipher. Digest is calculated by SHA algorithm for M1. The size of digest SHA-1 is 20 bytes. The size of digest SHA-256 is 32 bytes. The digest is too small so that we can use RSA to encrypt/decrypt it.


We can apply digital signature in secure boot, BIOS verifies if an OS loader is released by a OS vendor before running the OS loader. Before the OS vendor releases a version OS, the vendor signs the OS loader with private key and provides the public key to a BIOS vendor. Please refer the pages for the details of secure boot. The concept of secure boot comes from digital signature.


-Count

How to Draw a Beautiful Flow Chart?

This page describes how to draw a beautiful flow chart. Let's use "secure boot" as an example.

The below picture 1 is a standard flow chart that embeds a text in a rectangle. There is a problem that if the text is too long, we must enlarge the rectangle to contain the text.

Picture 1

We enhances the flow chart as the below picture 2. We just draws many beautiful fixed-size circulars that don't contain any texts. We put a text outside a circular. Now we liberate the limitation of writing texts. We can describe a longer text with more details. We can group these circulars and texts as a concept that is described by another big text. We can also write comments for a circular.



Picture 2

How do you feel the both flow charts?

-Count

Thursday, August 20, 2015

Use UEFI Shell Script to Automatically Test Your UEFI Module

We know that test program is very import for developing software components. It is not only for QA staff, but also for ourselves programmer to program quickly and to be quality.

Software developing is actually a serial of automatic processes. Code is an automatic process run by computer. It is more fast and exact than human brain. that is why a computer was developed to encrypt and decrypt messages. Test is a process we can do it automatically to reduce our effort and assurance the quality of software product. Design is a process, but we only design with our brain now. Maybe design will be delivered by a computer in the future. Programming is a process that is also be written by programmers, but Hollywood movies show us code can update itself.

This page focus on automatically testing UEFI module with UEFI shell script.

The idea is very simple. For example, we want to use a smart way to erase data on flash ROM with size in 4096-byte alignment. Flash ROM supports the following commands to erase data:

4K-Erase: Erasing data in 4K bytes
32K-Erase: Erasing data in 32k bytes
64K-Erase: Erasing data in 64k bytes

The performance is bad if we only use 4K-Erase command to erase large data. For example, To erase data with 1024K, we send 256 commands of 4K-Erase. If we use 64K-Erase command, we only sends 16 commands. We cannot always use 64K-Erase command because it is only be used in 64K alignment.

For example, if we want to erase 548K data at 100K offset, we only use the following 14 commands of 4K-Erase, 32K-Erase, and 64K-Erase.

100K Offset, 4K-Erase
104K Offset, 4K-Erase
108K Offset, 4K-Erase
112K Offset, 4K-Erase
116K Offset, 4K-Erase
120K Offset, 4K-Erase
128K Offset, 64K-Erase
192K Offset, 64K-Erase
256K Offset, 64K-Erase
320K Offset, 64K-Erase
384K Offset, 64K-Erase
448K Offset, 64K-Erase
512K Offset, 32K-Erase
544K Offset, 4K-Erase

We define the SmartFlashErase() for this requirement.

EFI_STATIS
EFIAPI
SmartFlashErase (
  IN UINT64 Offset
  IN UINT32 Size
  );

Our test program is SmartFlashEraseTest.efi with the following usage.

SmartFlashEraseTest.efi -offset offset -size -size

Out test script is SmartFlashEraseTest.nsh as below.

@echo [Case]
SmartFlashEraseTest.efi -offset 100k -size 548k
@if not %LastError% == 0 then
  @echo [Error] LastError = %LastError%
  goto exit
@endif
@echo [CheckPoint] Status = 0 (Success)

:exit

We can use redirect the NSH to a log file.
SmarFlashEraseTest.nsh > Case.log

The log file contains tags, [Case], [Error], [CheckPoint]. We can write a python program to analyze these tags. I'll write another page for this topic.

This way not only for UEFI shell, any environment that supports script can use the way to automatically test our software components.

-Count

Thursday, June 25, 2015

Use C Library in EDK II UEFI Driver

When we use C library in EDK II UEFI Driver, build failure occurs. Please refer UEFI原理與編程 for the detail. This page describes my way to solve it.

We want to use sprintf() of C library in UEFI driver to provide the driver information (e.g., build-date). I like to write the code as below,

#include <stdio.h>

EFI_STATUS
EFIAPI
LibCDxeEntryPoint (
  IN EFI_HANDLE ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  CHAR8 Info [256];
            
  sprintf (Info, "Build Date: %s", __DATE__);

  return EFI_SUCCESS;
}  

, but build failure occurs. Please,

1. Add the following statements in your package DSC file.

These statements declare that we will build C library for UEFI driver.

[LibraryClasses.Common.UEFI_DRIVER]  
  ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf
  FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf  
  SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
  
  LibC|StdLib/LibC/LibC_ForDriver.inf
  #LibC|StdLib/LibC/LibC.inf
  LibCType|StdLib/LibC/Ctype/Ctype.inf  
  LibLocale|StdLib/LibC/Locale/Locale.inf  
  LibSignal|StdLib/LibC/Signal/Signal.inf  
  LibStdio|StdLib/LibC/Stdio/Stdio.inf  
  LibStdLib|StdLib/LibC/StdLib/StdLib.inf
  LibString|StdLib/LibC/String/String.inf  
  LibTime|StdLib/LibC/Time/Time.inf  
  LibUefi|StdLib/LibC/Uefi/Uefi.inf  
  LibWchar|StdLib/LibC/Wchar/Wchar.inf  
  
  LibGen|StdLib/PosixLib/Gen/LibGen.inf  
  LibIIO|StdLib/LibC/Uefi/InteractiveIO/IIO.inf

  LibContainer|StdLib/LibC/Containers/ContainerLib.inf   
  
  LibGdtoa|StdLib/LibC/gdtoa/gdtoa.inf  
  DevConsole|StdLib/LibC/Uefi/Devices/daConsole.inf
  DevUtility|StdLib/LibC/Uefi/Devices/daUtility.inf

2. Use LibC_ForDriver.inf instead of LibC.inf.

We removes ShellCEntryLib from LibC_ForDriver.inf. If we don't do it, the error occurs.

error 1001: Module type [UEFI_DRIVER] is not supported by library instance [...\MdePkg\Library\UefiApplicationEntryPoint\UefiApplicationEntryPoint.inf]

LibC_ForDriver.inf

[LibraryClasses]
#  ShellCEntryLib
  UefiLib
  BaseLib
  BaseMemoryLib
  MemoryAllocationLib
  LibStdLib
  LibStdio
  LibString
  DevConsole

3. Add dummy main().

After we follow the steps, the link error occurs, the main symbol is not defined. Please add dummy main() in your driver.

int
main (
  IN int Argc,
  IN char **Argv
  )
{
  return 0;
}

Now we can build the UEFI driver with C standard library.

-Count

Wednesday, June 17, 2015

Make Your Code Portable

The following are principles to make your code portable.

1) Use C instead of C++.

If you want your library more portable, please use C instead of C++.

2) Use C standard library.

Because EDK II supports C standard library, we just use it.

Please refer Use C Library in EDK II UEFI Driver

3) As far as possible, allocate memory space for a returned parameter before calling a public function.

For example,

EFI_STATUS
CompressData (
  IN UINT8 *Buffer,
  IN UINTN BufferSize,
  OUT UINT8 **Output,
  OUT UINTN *OutputSize
  );

This Compress function allocates memory space for the returned Output. The problem is that the memory management mechanisms are different in environments. In UEFI DXE environment, gBS->AllocatePool() supports the memory space allocation. In SMM mode, UEFI provides SmmAllocatePool(). In Windows KMDF, we should use WdfMemoryCreate to allocate memory space.

Therefore please rework the function as below otherwise we need to create many stubs to wrap the memory management functions for different environments.

EFI_STATUS
GetCompressedDataSize (
  IN UINT8 *Buffer,
  IN UINTN BufferSize,
  OUT UINTN *OutputSize
  );

EFI_STATUS
CompressData (
  IN UINT8 *Buffer,
  IN UINTN BufferSize,
  OUT UINT8 *Output,
  IN UINTN OutputSize
  );

4) The sizes of fields in C struct must be determined.

Don't use UINTN or ulong, the sizes are undetermined, in your struct. If you define struct in UEFI environment, please use UINT8, UINT16, UINT32, CHAR8, CHAR16 of which sizes are always same in IA32 and X64.

-Count

Use C++ in EDK II

There are two ways to write C++ code in EDK II.
1. Make your whole files as CPP
2. Low level files are CPP, High level files are C.

I prefer the second way because we always use C to develop UEFI driver and sometimes we want to reuse C++ components.

Please refer the UEFI原理与编程 for the detail of C++ in EDK II, where my idea comes from.

Below is my source code of UEFI application that uses C++ directly.

CppTest.inf -

The INF file defines the UEFI application, CppTest.c calls C functions of Wrap.cpp that wraps C++ of MyObject.cpp.

[Sources]
  CppTest.c
  MyObject.cpp
  MyObject.h
  Wrap.cpp
  Wrap.h

MyObject.h -

Declare the class MY_OBJECT.

class MY_OBJECT
{
  private:
    UINT8 Value1;
    UINT8 Value2;

  public:

    MY_OBJECT (UINT8 Value1, UINT8 Value2);
    UINT8 GetValue1 ();
    UINT8 GetValue2 ();    
    ~MY_OBJECT ();
};

MyObject.cpp - 

Implement the class MY_OBJECT.

MY_OBJECT::MY_OBJECT (
  IN UINT8 Value1, 
  IN UINT8 Value2
  )
{
  this->Value1 = Value1;
  this->Value2 = Value2;
}

UINT8 
MY_OBJECT::GetValue1 ()
{
  return this->Value1;
}

UINT8 
MY_OBJECT::GetValue2 ()
{
  return this->Value2;
}

MY_OBJECT::~MY_OBJECT ()
{
  this->Value1 = 0;
  this->Value2 = 0;
}

Wrap.h - 

Declare the C functions that wraps the class MY_OBJECT. The term, extern "C", declares that the following functions are name mangling in C. The value of __cplusplus is 1 when CPP file includes the H file so that the term enables. That is why we use #ifdef __cplusplus to wrap the term.

#ifdef __cplusplus
extern "C" {
#endif

VOID 
EFIAPI
CreateMyObject (VOID);

EFI_STATUS
EFIAPI
GetValuesOfMyObject (
  OUT CHAR8 *Buffer,
  IN UINTN BufferSize
  );
  
VOID 
EFIAPI
DestroyMyObj (VOID);  

#ifdef __cplusplus
}
#endif

Wrap.cpp -

Implement the C wrapping functions. We use the term, extern "C", previous to each function.It is interesting that we implement new and delete operators. If we don't do it, link errors about new and delete occurs.

MY_OBJECT *mMyObject;

void * operator new (size_t Size)
{
  VOID *RetVal;
  RetVal = AllocatePool (Size);
  return RetVal;
}

void operator delete (void *p)
{
  FreePool (p);
}

extern "C"
VOID 
EFIAPI
CreateMyObject (VOID)
{
  mMyObject = new MY_OBJECT (10, 20);
}

extern "C"
EFI_STATUS
EFIAPI
GetValuesOfMyObject (
  OUT CHAR8 *Buffer,
  IN UINTN BufferSize
  )
{
  UINTN i;
  i = 0;

  if (i == BufferSize) {
    return EFI_BUFFER_TOO_SMALL;
  }
  Buffer[i++] = mMyObject->GetValue1 ();

  if (i == BufferSize) {
    return EFI_BUFFER_TOO_SMALL;
  }
  Buffer[i++] = mMyObject->GetValue2 ();
  
  return EFI_SUCCESS;
}  

extern "C"
VOID 
EFIAPI
DestroyMyObj (VOID)
{
  delete mMyObject;
}

CppTest.c -

The C main program that tests the C wrapping function.

int
main (
  IN int Argc,
  IN char **Argv
  )
{
  UINT8 Buffer [10];
  MY_STRUCT *MyStruct;
  
  CreateMyObject ();
  
  GetValuesOfMyObject (Buffer, 10);
  printf ("Buffer [0] = %d\n", Buffer [0]);
  printf ("Buffer [1] = %d\n", Buffer [1]);
  
  DestroyMyObj ();
  
  return 0;
}


-Count

Wednesday, June 3, 2015

Link C++ Library in EDK II Module

It is hard to write C++ code in cpp and put it in EDK II INF file to build a driver with EDK2 build system because build failures occur. There are two ways to solve it.

1) We use native C++ compiler (e.g., VC++) to pack C++ code in a library with wrapped pure C function first, copy the library in your UEFI driver source code, and put the lib file in the EDK II INF file.

2) We write C++ code in a cpp file and to put the cpp file in the driver INF file.

This page describes the first way. I will describe the second way in another page, Use C++ in EDK II.

When we select the first way, build failure occurs on link errors,

error LNK2001: unresolved external symbol "void __cdecl `eh vector constructor iterator' ...

error LNK2001: unresolved external symbol "void __cdecl `eh vector destructor iterator' ...

The root cause is, we use new/delete operator to create/destroy an object array. VC++ needs the both symbols to handle the new-delete-object-array operations. The errors don't occur when building a Windows program because Windows environment provides symbols in a Windows specific library. But the link error occurs in UEFI building environment because it lacks the library.

I cannot find a regular and simple solution. My workaround is to modify C++ code to avoid to use new operator in class array.

MY_OBJ *ObjArray = new MY_OBJ [100];
delete[] ObjArray;                  

I use malloc/free instead of new/delete as below for an object array;

MY_OBJ **ObjArray = (MY_OBJ **) malloc (sizeof (MY_OBJ *) * 100);
for (int i = 0; i < 100; i++) {                                  
  ObjArray [i] = new MY_OBJ;                                     
}                                                                
                                                                 
for (int i = 0; i < 100; i++) {                                  
  delete ObjArray [i];                                           
}                                                                
free (ObjArray);                                                 

The regular solution is to use C struct instead of C++ class for platform portability.

Another link error occurs as below.

error LNK2001: unresolved external symbol __CxxFrameHandler3

The symbol __CxxFrameHandler3 is to check buffer overflow. The UEFI building environment lacks the symbol so that the link error occurs.

My solution is to create a dummy __CxxFrameHandler3 function in UEFI source code to avoid the link error.

void __CxxFrameHandler3 (void)
{
  return;
}

-Count