6

I am creating a Ubuntu 12.04 docker image that has gcc 4.8.5. I am getting gcc 4.8.5 source and building it myself. This container will be running on a Ubuntu 18.04 host.

Referencing the code at the bottom, if I don't put this in the dockerfile and run the same commands after starting the container, the build works fine, however if I use RUN instead in the dockerfile, I get the following build error

In file included from /usr/include/stdio.h:28:0,
             from ../../../gcc-4.8.5/libgcc/../gcc/tsystem.h:87,
             from ../../../gcc-4.8.5/libgcc/libgcc2.c:27:
/usr/include/features.h:324:26: fatal error: bits/predefs.h: No such 
file or directory
#include <bits/predefs.h>
                      ^

The problem seems to stem from the ./gcc-4.8.5/configure call. When run inside the container i get:

checking build system type... i686-pc-linux-gnu

When put in the dockerfile i get:

checking build system type... x86_64-unknown-linux-gnu

Can someone fill in my understanding of the RUN in dockerfiles because i feel like i am missing something about how its working. I was under the impression that those commands would run in the previous layer? But it seems like they are running on my host.

## Get gcc 4.8.5 and build it
RUN wget ftp://gcc.gnu.org/pub/gcc/releases/gcc-4.8.5/gcc-4.8.5.tar.gz \
&& tar xzf gcc-4.8.5.tar.gz && \
cd gcc-4.8.5 && \
./contrib/download_prerequisites && \
cd .. && mkdir gccbuild && cd gccbuild && \
../gcc-4.8.5/configure \
--prefix="/opt/gcc" \
--enable-shared --with-system-zlib --enable-threads=posix \
--enable-__cxa_atexit --enable-checking --enable-gnu-indirect-function \
--enable-languages="c,c++" --disable-bootstrap \
&& make all && make install 

EDIT:

docker build -t 12.04_builder - < dockerfile
docker run -i -t 12.04_builder

Complete dockerfile:

FROM jnickborys/i386-ubuntu:12.04

RUN apt-get update && \ 
    apt-get install -y \ 
      wget \
      build-essential \
      libssl-dev \
      git \
      asciidoc \
      libpulse-dev \
      libasound2-dev \
      libpcsclite-dev 

## Get latest cmake that has a 32-bit version
RUN wget https://github.com/Kitware/CMake/releases/download/v3.6.3/cmake-3.6.3-Linux-i386.sh && \ 
    chmod +x cmake-3.6.3-Linux-i386.sh && \ 
    ./cmake-3.6.3-Linux-i386.sh --skip-license --prefix=/usr

## Get gcc 4.8.5 and build it
RUN wget ftp://gcc.gnu.org/pub/gcc/releases/gcc-4.8.5/gcc-4.8.5.tar.gz \
    && tar xzf gcc-4.8.5.tar.gz && \
    cd gcc-4.8.5 && \
    ./contrib/download_prerequisites && \
    cd .. && mkdir gccbuild && cd gccbuild && \
    ../gcc-4.8.5/configure \
    --prefix="/opt/gcc" \
    --enable-shared --with-system-zlib --enable-threads=posix \
    --enable-__cxa_atexit --enable-checking --enable-gnu-indirect-function \
    --enable-languages="c,c++" --disable-bootstrap 
    && make all && make install
  • Can you share full `Dockerfile` and the way you start the container together with command line inside container – grapes Jan 29 '19 at 08:15
  • I have added the complete file with how i am building it and running it – JohnSmith61 Jan 29 '19 at 19:52
  • Not sure if this will help. But maybe try installing `gcc-multilib` as described here - https://stackoverflow.com/a/12615029/1421222 – Bless Jan 30 '19 at 05:58
  • Did try that, it doesn't work. If that was the case then running the commands inside the container would fail too. – JohnSmith61 Jan 30 '19 at 18:45

1 Answers1

3

First of all, a little bit of background: the platform detection script which runs during the build uses uname(1) utility (thus uname(2) system call) to identify the hardware it runs on:

root@6e4b69adfd4c:/gcc-4.8.5# grep 'uname -m' config.guess 
UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown

On your 64-bit machine uname -m returns x86_64. However, there is a system call which allows overriding this result: personality(2). When the process calls personality(2), it and its subsequent forks (children) start seeing the fake results when calling uname(2). So, there is the possibility to ask the kernel to provide the fake hardware information in uname(2).

The base image you use (jnickborys/i386-ubuntu:12.04) contains the 32-bit binaries and defines the entrypoint /usr/bin/linux32, which calls personality(PER_LINUX32) to ask the kernel to pretend that it runs on 32-bit hardware and to return i686 in uname(2) (this may be checked using docker inspect and strace respectively). This makes possible to pretend that the containerized process runs in 32-bit environment.

What is the difference between executing the build in RUN directive and manually in the container?

When you execute the build in RUN, Docker does not use the entrypoint to run the commands. It uses what is specified in the SHELL directive instead (default is /bin/sh -c). This means that the personality of the shell running the build is not altered, and it (and the child processes) sees the real hardware information - x86_64, thus, you get x86_64-unknown-linux-gnu build system type in 32-bit environment and the build fails.

When you run the build manually in the container (e.g. after starting it using docker run -it jnickborys/i386-ubuntu:12.04 and then performing the same steps as in the Dockerfile), the entrypoint is called, thus, the personality is altered, and the kernel starts reporting that it runs on 32-bit hardware (i686), so you get i686-pc-linux-gnu build system type, and the build runs correctly.

How to fix this? Depends on what do you want. If your goal is to build gcc for 64-bit environment, just use the 64-bit base image. If you want to build for 32-bit environment, one of your options is to alter the SHELL being used for RUNs before these RUNs:

SHELL ["/usr/bin/linux32", "/bin/sh", "-c"]

This will make Docker execute RUNs with altered personality, so, the build system type will be detected correctly (i686-pc-linux-gnu) and the build will succeed. If required, you may change the SHELL back to /bin/sh -c after the build.

Danila Kiver
  • 3,418
  • 1
  • 21
  • 31