Some newer Atmel microcontrollers are not fully supported by GCC, also sometimes there are errata that did not make it into GCC yet.
Atmel/Microchip offers so called "Compiler Packs" for download from their homepage: http://packs.download.atmel.com/ - those packs contain the most recent header files and linker scripts that are used by GCC to compile sources for a target microcontroller.
The download page provides compiler packs for several families of MCUs - simply download the one that includes the MCU you are using.
Below I describe only what needs to be changed compared to using plain AVR-GCC. There are several tutorials online to describe using GCC without compiler packs.
This is an example command line to compile a C source file for an AtMega3208:
AVRBASE=$HOME/compiler-packs/mega AVRPREFIX=$AVRBASE/gcc/dev/mega3208 DEFINES="-D__AVR_DEV_LIB_NAME__=m3208 -D F_CPU=20000000" avr-gcc -Wall -Os -ffunction-sections -fdata-sections -mmcu=mega3208 -B $AVRPREFIX -isystem $AVRBASE/include $DEFINES -c -o hello.o hello.c
The AVRBASE environment variable above assumes that the compiler pack for the AtMega family of MCUs got unpacked into $HOME/compiler-packs/mega - you need to adjust this for your own environment.
The strings m3208 and mega3208 both describe the AtMega3208 MCU. At various places "AtMega" is either abbreviated as "mega" or even as "m". The "AtXMega" chips are abbreviated as "xmega" and "x". The "AtTiny" family as "tiny" or "t". You need to replace those strings with something that matches your own target chip.
The -Os switch optimizes for space. -Wall activates all warnings - you may want to change this to suppress some warnings that you do not need.
The -B switch tells GCC where to find the description of the chip and -mmcu tells GCC which chip to select. The -isystem switch then tells GCC where to find the platform include files, so that it does not attempt to use the older includes that come with GCC itself.
The -ffunction-sections -fdata-sections switches are needed because AVR microcontrollers have separate program and data memory - this tells GCC to separate code and data in the binary.
The contents of $DEFINES are described below.
For C++ you would use avr-g++ instead of avr-gcc and add parameters like -fno-exceptions -std=c++17 - disabling exceptions (since they use up a lot of space in program memory and on the stack) and selects a C++ standard version.
The compile call above contained this line:
DEFINES="-D__AVR_DEV_LIB_NAME__=m3208 -D F_CPU=20000000"
As usual the source then simply includes avr/io.h and relies on it to do its magic:
#include <avr/io.h>
However, this magic needs the help of those defines. The __AVR_DEV_LIB_NAME__ symbol needs to contain the name of the microcontroller in case AVR-LibC does not know it yet - which is rather likely for some of the newer ones. It then uses the symbol to find and load the correct header file from the compiler pack, which then contains all the register definitions for that specific microcontroller.
Like with standard GCC the F_CPU symbol tells AVR-LibC the clock frequency of the microcontroller, so that it can tune the sleep functions.
Like the compile, the linker needs the -B parameter to find its link scripts and the -mmcu parameter to select the target chip. The chip specific link script contains the memory layout of the chip as well as a recipe on how to arrange binary symbols in memory.
avr-gcc -Os -Wl,--gc-sections -B $AVRPREFIX -mmcu=mega3208 -o firmware.elf *.o
Replace avr-gcc with avr-g++ if the binary contains any C++.