CONFIG_X86_64 flag and its usage - linux-kernel

I just checked my linux box's config file /boot/config_$(uname -r) and I found both of these flags are defined:
CONFIG_X86_64=y
CONFIG_X86=y
Shouldn't these 2 flags be exclusive to each other?
In addition, I am wondering whether these 2 flags should be used in kernel only because I saw many
#ifdef CONFIG_X86_64
in kernel source code. Can user space application use this flag also?
In addition, since processor can be changed to compatibility mode from 64-bit mode. If this change happens, for code that depend on CONFIG_X86_64 will all fail at run time, right? How does application (kernel or user space) to detect whether machine is in 64 bit or compatibility mode?
Thanks.

CONFIG_X86 is the flag targetting the architecture, the whole x86 family.
This includes both the 32-bit and 64-bit processors.
This can be seen by looking at the latest kernel (at the time of writing it is 4.15.1) Kconfig file1
# SPDX-License-Identifier: GPL-2.0
# Select 32 or 64 bit
config 64BIT
bool "64-bit kernel" if ARCH = "x86"
default ARCH != "i386"
---help---
Say yes to build a 64-bit kernel - formerly known as x86_64
Say no to build a 32-bit kernel - formerly known as i386
config X86_32
def_bool y
depends on !64BIT
#... other options removed
config X86_64
def_bool y
depends on 64BIT
In this file, config options are stripped of the CONFIG_ prefix.
The CONFIG_X86_64 is defined iif CONFIG_64BIT is defined, otherwise CONFIG_X86_32 is.
Look at the depends on declaration to see it.
In a 64-bit kernel this command cat /boot/config-$(uname -r) | grep 'CONFIG_64BIT' should return CONFIG_64BIT=y.
This is also confirmed in this answer for a question on how to make a 32-bit config into a 64-bit one.
The antonym of CONFIG_X86_64 is thus CONFIG_X86_32.
TL;DR CONFIG_X86 is defined for all x86 processors, either bitness. CONFIG_X86_64 is defined only for the subset of x86 processors supporting AMD64/IA32e.
1 This link may change at any time soon. See this.

Related

How to use constants created with .equ in a second assembler file?

Context
I'm working through some examples in a book by Johnathan Bartlett titled "Learn to Program with Assembly" (2021). The author assumes a linux environment. I'm on OSX (Monterey). He's using gcc. I've got clang (v 13.1.6). In chapter 7 the author introduces laying out data records.
To facilitate this, he uses the .equ directive to define some constants in a file titled persondata.s which happens to only contain a data segment. For example:
# Describe the components of the struct
.globl WEIGHT_OFFSET, HAIR_OFFSET, HEIGHT_OFFSET, AGE_OFFSET .equ WEIGHT_OFFSET, 0
.equ HAIR_OFFSET, 8
.equ HEIGHT_OFFSET, 16
.equ AGE_OFFSET, 24
In another file, tallest.s, he makes use of the HEIGHT_OFFSET constant to access the height of a person record. This file has only a text segment.
movq HEIGHT_OFFSET(%rbx), %rax
The Problem
When I assemble tallest.s using the built-in tools on OSX, the assembler complains that I'm trying to use 32-bit absolute addressing in 64-bit mode.
The Question
How is this supposed to work on OSX? How am I supposed to make use of .equ defined constants?
Things I Tried
If I merge these two files into one file, then assembler doesn't complain. It treats HEIGHT_OFFSET as the constant that it is.
I presume the idea is to have constants defined along with the data, and then make use of those constants in code to avoid 'magic numbers'. Sounds like a good idea.
I tried assembling, linking, and running this code using the book's docker image (johnnyb61820/linux-assembly). It works. No complaints. Some details
# as -v
GNU assembler version 2.31.1 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.31.1
^C
# ld -v
GNU ld (GNU Binutils for Debian) 2.31.1
# uname -a
Linux eded2adb9c06 5.10.124-linuxkit #1 SMP Thu Jun 30 08:19:10 UTC 2022 x86_64 GNU/Linux
So it works as written under that set-up. Just not under my set-up which is clang (v 13.1.6).
Based on the fact that this works in the linuxkit docker image, I thought to install gcc via homebrew on my machine. This got me version 12.2.0 of gcc, which I used to try and compile/link my files. It also thinks HEIGHT_OFFSET is a problem due to 32-bit absolute addressing in 64-bit mode.
Based on the output of name -a in the docker image, I'm guessing it is 64 bit. Linux eded2adb9c06 5.10.124-linuxkit #1 SMP Thu Jun 30 08:19:10 UTC 2022 x86_64 GNU/Linux
Oddly enough, it doesn't complain about 32-bit absolute addressing not being supported. Under OSX, I had to make everything rip-relative to access any static-data (true for both gcc and clang). Makes me wonder what it is doing with these addresses.
As a possibly final note, under OSX yasm also doesn't like me using .equ defined constants from another file. It complains about wanting to make use of "32 bit absolute relocations" in 64 bit mode. GCC (12.2.0) and llvm-mc (13.0.1) also take issue with the HEIGHT_OFFSET constant.

Booting custom kernel on xeon-phi

I am trying to boot a custom kernel on Xeon-phi instead of the default Linux kernel. At this link, I found a way to cross compile my kernel which compiles successfully using k1om-mpss-linux-gcc cross compiler. Is cross compiling enough ? I get the error
mykernel.img is not a k1om Linux bzImage
Edit:
So, I used /usr/linux-k1om-4.7/bin/x86_64-k1om-linux-gcc compiler to compile a simple helloworld.c program and the kernel source. I get two different types of results for objdump -f on the executables.
for helloworld.c:
hello: file format elf64-k1om
architecture: k1om, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000000400400
for mykernel:
mykernel: file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0010000c
I compiled using the same compiler, yet they show different architectures. What is the reason for this ?
The first thing to do is figure out what mykernel.img is. Try running file on it.
$ file /opt/mpss/3.4/sysroots/k1om-mpss-linux/boot/vmlinux-2.6.38.8+mpss3.4
/opt/mpss/3.4/sysroots/k1om-mpss-linux/boot/vmlinux-2.6.38.8+mpss3.4: ELF 64-bit LSB executable, version 1 (SYSV), statically linked, BuildID[sha1]=0xa4c16ee85c11aca4e78dc4ae46d3827fb74289c1, not stripped
$ objdump -f /opt/mpss/3.4/sysroots/k1om-mpss-linux/boot/vmlinux-2.6.38.8+mpss3.4
/opt/mpss/3.4/sysroots/k1om-mpss-linux/boot/vmlinux-2.6.38.8+mpss3.4: file format elf64-k1om
architecture: k1om, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000001000000
The answer to your original question - no, unfortunately, it is not as simple as just cross-compiling. There were a number of changes made to the kernel that comes with the MPSS. I don't know all the changes but a big one that I do know is that they had to add support for the larger register set on the coprocessor in order to be able to save state on a context switch.
As to why the file format is elf32-i386 instead of elf32-k1om -
The web site you referenced referred to recompiling the kernel that came with the MPSS after possibly make a few changes in the files. You'll notice that they also copied over a configuration file for the installed version of the kernel. So they had all the files to remake the kernel exactly as it had been made.
I suspect that, in your case, either a) there was a configuration script of some sort in your source directory that picked up the architecture you were running on and caused confusion when the makefile ran or b) your makefile had no idea what k1om was. In either case, it fell back to what it believed to the the lowest common denominator i386. As I say, this is just a suspicion on my part but a careful reading of your makefiles should lead to the answer.

what tells 32-bit gcc on a 64-bit system, to make 64-bit binary

I have a 64-bit system, but gcc is 32-bits and when I do
>./gcc -c foobar.c
it makes foobar.o which is 64-bits. OK, but how does it know to do that? Based on what environment setting does it know to produce 64-bit object, and where is that documented??
Come to think about it, it is strange that it does that, is it not?? But file utility clearly says, gcc is 32 bits and foobar.o is 64 bits. ( I moved everything to the same directory so it would not be confused. )
I also checked the 3 dynamically linked libraries that it reads: libc, libm and libz and they are also all 32 bits.
To clarify, I don't want to know, how to make it do 32 bits. I want to know, what is it looking at now that it makes it do 64. That is my question, not how to force it the other way around.
When GCC is configured three different systems can be specified:
build: - the system where GCC is going to be built (probably not
relevant to your question)
host: - the system where GCC is going to
be executed (32 bit in your case)
target: the system where binaries, produced by GCC are going to be executed (64 bit in your case)
You can see how your GCC was configured by running:
gcc -v
command (look for --{build,host,target} options.

Building 32bit binary for ARM on 64bit CentOS

It seems that gcc doesn't accept -m32 option for ARM target. I am not sure how gcc behaves on 64bit Linux, but does it automatically generate 32bit binaries if gcc is of ELF32 running on 64bit Linux?
If so, is there any workaround?
Thanks in advance.
You need to use a cross-compiler to compile for ARM from your host running either x86 or x86_64, the reason being your host and target are 2 totally independent architectures.
The cross compiler would usually be configured to output only a 32-bit or 64-bit binary for ARM (not both). Most ARM device applications make use of only 32-bit and so using an arm cross-compiler without any extra arguments would build 32-bit binaries.
Toolchains have other -m flags to specify machine type such as armv7, arm cortex a-8, etc. for further optimization. You need to look at the documentation of the ARM cross compiler.
As for getting the correct toolchain which works for your target and runs under CentOS, it is better to start at the website of the vendor of the target device.
The -m32 option provided by the x86_64 version of gcc makes gcc compile 32-bit binaries instead of 64-bit since the x86 instruction set and the x86_64 (AMD64 or Intel EMT64) are quite similar. Especially the fact that it allows executing 32-bit instructions in 64-bit mode quite easily.

seg fault when running arm-elf-gcc compiled code

Using MacPorts i have just installed arm-elf-gcc on to my MacBook Pro. This worked flawlessly and all seems to run fine.
However, after compiling a simple hello world test program in C and C++ and trying to run either on the target board (an ARM9 based board running Debian Linux) they immediately seg fault.
I'm a bit stuck as how to go about debugging this, as the target board has limited tools available and no gdb. I have successfully built and run other code using a Linux hosted cross compiler so it should work.
Any ideas?
Following the suggestion I have built and run gdbserver, I get the following in gdb on the host:
Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()
I thought it may be a problem with the standard c libs so I removed any calls and have just an empty main that return 0, it is compiled with -Wall -g hello-arm.cpp -static. As a test I compiled the same source with a Linux hosted cross compiler and it runs and exits fine. The only difference I can see is the that Linux compiled version is over twice the size and the difference in output from the file command:
arm-elf-gcc: ELF 32-bit LSB executable, ARM, version 1, statically linked, not stripped
arm-*-linux: ELF 32-bit LSB executable, ARM, version 1, statically linked, for GNU/Linux 2.4.18, not stripped
The usual method of debugging in this situation is to run gdbserver on the target board, and connect to it (via ethernet) with gdb running on a host computer.
Alternately, you could try comparing the assembly in a Mac-compiled "Hello World" program and a (working) Linux-compiled one to see what's different.
After digging around for a couple of days I am starting to understand a bit more about embedded compilers. I wasn't really sure of the difference between arm-elf-gcc installed via MacPorts and the arm-unknown-linux toolchain I had installed on my Linux box. I just came across a pdf titled "An introduction to the GNU compiler" which contains the following paragraph:
Important: Using the GNU Compiler to
create your executable is not quite
the same as using the GNU Linker,
arm-elf-ld, yourself. The reason is
that the GNU Compiler automatically
links a number of standard system
libraries into your executable. These
libraries allow your program to
interact with an operating system, to
use the standard C library functions,
to use certain language features and
operations (such as division), and so
on. If you wish to see exactly which
libraries are being linked into the
executable, you should pass the
verbose flag
-v to the compiler.
This has important implications for
embedded systems! Such systems do not
usually have an operating system.
This means that linking in the system
libraries is almost always
meaningless: if there is no operating
system, for example, then calling the
standard printf function does not make
much sense.
So when I get back to my dev machine later I will determine the libraries linked in with the Linux build and add them to the arm-elf-gcc build.
I'll update this when I have more information but I just want to document my findings in case any one else has these problems.

Resources